Imported Upstream version 7.59.0
[platform/upstream/curl.git] / lib / conncache.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9  * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
10  *
11  * This software is licensed as described in the file COPYING, which
12  * you should have received as part of this distribution. The terms
13  * are also available at https://curl.haxx.se/docs/copyright.html.
14  *
15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16  * copies of the Software, and permit persons to whom the Software is
17  * furnished to do so, under the terms of the COPYING file.
18  *
19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20  * KIND, either express or implied.
21  *
22  ***************************************************************************/
23
24 #include "curl_setup.h"
25
26 #include <curl/curl.h>
27
28 #include "urldata.h"
29 #include "url.h"
30 #include "progress.h"
31 #include "multiif.h"
32 #include "sendf.h"
33 #include "conncache.h"
34 #include "share.h"
35 #include "sigpipe.h"
36 #include "connect.h"
37
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42
43 #ifdef CURLDEBUG
44 /* the debug versions of these macros make extra certain that the lock is
45    never doubly locked or unlocked */
46 #define CONN_LOCK(x) if((x)->share) {                                   \
47     Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \
48     DEBUGASSERT(!(x)->state.conncache_lock);                            \
49     (x)->state.conncache_lock = TRUE;                                   \
50   }
51
52 #define CONN_UNLOCK(x) if((x)->share) {                                 \
53     DEBUGASSERT((x)->state.conncache_lock);                             \
54     (x)->state.conncache_lock = FALSE;                                  \
55     Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT);                     \
56   }
57 #else
58 #define CONN_LOCK(x) if((x)->share)                                     \
59     Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE)
60 #define CONN_UNLOCK(x) if((x)->share)                   \
61     Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT)
62 #endif
63
64 static void conn_llist_dtor(void *user, void *element)
65 {
66   struct connectdata *data = element;
67   (void)user;
68
69   data->bundle = NULL;
70 }
71
72 static CURLcode bundle_create(struct Curl_easy *data,
73                               struct connectbundle **cb_ptr)
74 {
75   (void)data;
76   DEBUGASSERT(*cb_ptr == NULL);
77   *cb_ptr = malloc(sizeof(struct connectbundle));
78   if(!*cb_ptr)
79     return CURLE_OUT_OF_MEMORY;
80
81   (*cb_ptr)->num_connections = 0;
82   (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
83
84   Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor);
85   return CURLE_OK;
86 }
87
88 static void bundle_destroy(struct connectbundle *cb_ptr)
89 {
90   if(!cb_ptr)
91     return;
92
93   Curl_llist_destroy(&cb_ptr->conn_list, NULL);
94
95   free(cb_ptr);
96 }
97
98 /* Add a connection to a bundle */
99 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
100                                 struct connectdata *conn)
101 {
102   Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn,
103                          &conn->bundle_node);
104   conn->bundle = cb_ptr;
105   cb_ptr->num_connections++;
106   return CURLE_OK;
107 }
108
109 /* Remove a connection from a bundle */
110 static int bundle_remove_conn(struct connectbundle *cb_ptr,
111                               struct connectdata *conn)
112 {
113   struct curl_llist_element *curr;
114
115   curr = cb_ptr->conn_list.head;
116   while(curr) {
117     if(curr->ptr == conn) {
118       Curl_llist_remove(&cb_ptr->conn_list, curr, NULL);
119       cb_ptr->num_connections--;
120       conn->bundle = NULL;
121       return 1; /* we removed a handle */
122     }
123     curr = curr->next;
124   }
125   return 0;
126 }
127
128 static void free_bundle_hash_entry(void *freethis)
129 {
130   struct connectbundle *b = (struct connectbundle *) freethis;
131
132   bundle_destroy(b);
133 }
134
135 int Curl_conncache_init(struct conncache *connc, int size)
136 {
137   int rc;
138
139   /* allocate a new easy handle to use when closing cached connections */
140   connc->closure_handle = curl_easy_init();
141   if(!connc->closure_handle)
142     return 1; /* bad */
143
144   rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
145                       Curl_str_key_compare, free_bundle_hash_entry);
146   if(rc) {
147     Curl_close(connc->closure_handle);
148     connc->closure_handle = NULL;
149   }
150   else
151     connc->closure_handle->state.conn_cache = connc;
152
153   return rc;
154 }
155
156 void Curl_conncache_destroy(struct conncache *connc)
157 {
158   if(connc)
159     Curl_hash_destroy(&connc->hash);
160 }
161
162 /* creates a key to find a bundle for this connection */
163 static void hashkey(struct connectdata *conn, char *buf,
164                     size_t len) /* something like 128 is fine */
165 {
166   const char *hostname;
167
168   if(conn->bits.socksproxy)
169     hostname = conn->socks_proxy.host.name;
170   else if(conn->bits.httpproxy)
171     hostname = conn->http_proxy.host.name;
172   else if(conn->bits.conn_to_host)
173     hostname = conn->conn_to_host.name;
174   else
175     hostname = conn->host.name;
176
177   DEBUGASSERT(len > 32);
178
179   /* put the number first so that the hostname gets cut off if too long */
180   snprintf(buf, len, "%ld%s", conn->port, hostname);
181 }
182
183 void Curl_conncache_unlock(struct connectdata *conn)
184 {
185   CONN_UNLOCK(conn->data);
186 }
187
188 /* Returns number of connections currently held in the connection cache.
189    Locks/unlocks the cache itself!
190 */
191 size_t Curl_conncache_size(struct Curl_easy *data)
192 {
193   size_t num;
194   CONN_LOCK(data);
195   num = data->state.conn_cache->num_conn;
196   CONN_UNLOCK(data);
197   return num;
198 }
199
200 /* Returns number of connections currently held in the connections's bundle
201    Locks/unlocks the cache itself!
202 */
203 size_t Curl_conncache_bundle_size(struct connectdata *conn)
204 {
205   size_t num;
206   CONN_LOCK(conn->data);
207   num = conn->bundle->num_connections;
208   CONN_UNLOCK(conn->data);
209   return num;
210 }
211
212 /* Look up the bundle with all the connections to the same host this
213    connectdata struct is setup to use.
214
215    **NOTE**: When it returns, it holds the connection cache lock! */
216 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
217                                                  struct conncache *connc)
218 {
219   struct connectbundle *bundle = NULL;
220   CONN_LOCK(conn->data);
221   if(connc) {
222     char key[128];
223     hashkey(conn, key, sizeof(key));
224     bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
225   }
226
227   return bundle;
228 }
229
230 static bool conncache_add_bundle(struct conncache *connc,
231                                  char *key,
232                                  struct connectbundle *bundle)
233 {
234   void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
235
236   return p?TRUE:FALSE;
237 }
238
239 static void conncache_remove_bundle(struct conncache *connc,
240                                     struct connectbundle *bundle)
241 {
242   struct curl_hash_iterator iter;
243   struct curl_hash_element *he;
244
245   if(!connc)
246     return;
247
248   Curl_hash_start_iterate(&connc->hash, &iter);
249
250   he = Curl_hash_next_element(&iter);
251   while(he) {
252     if(he->ptr == bundle) {
253       /* The bundle is destroyed by the hash destructor function,
254          free_bundle_hash_entry() */
255       Curl_hash_delete(&connc->hash, he->key, he->key_len);
256       return;
257     }
258
259     he = Curl_hash_next_element(&iter);
260   }
261 }
262
263 CURLcode Curl_conncache_add_conn(struct conncache *connc,
264                                  struct connectdata *conn)
265 {
266   CURLcode result;
267   struct connectbundle *bundle;
268   struct connectbundle *new_bundle = NULL;
269   struct Curl_easy *data = conn->data;
270
271   /* *find_bundle() locks the connection cache */
272   bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
273   if(!bundle) {
274     int rc;
275     char key[128];
276
277     result = bundle_create(data, &new_bundle);
278     if(result) {
279       goto unlock;
280     }
281
282     hashkey(conn, key, sizeof(key));
283     rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
284
285     if(!rc) {
286       bundle_destroy(new_bundle);
287       result = CURLE_OUT_OF_MEMORY;
288       goto unlock;
289     }
290     bundle = new_bundle;
291   }
292
293   result = bundle_add_conn(bundle, conn);
294   if(result) {
295     if(new_bundle)
296       conncache_remove_bundle(data->state.conn_cache, new_bundle);
297     goto unlock;
298   }
299
300   conn->connection_id = connc->next_connection_id++;
301   connc->num_conn++;
302
303   DEBUGF(infof(conn->data, "Added connection %ld. "
304                "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
305                conn->connection_id, (curl_off_t) connc->num_conn));
306
307   unlock:
308   CONN_UNLOCK(data);
309
310   return result;
311 }
312
313 void Curl_conncache_remove_conn(struct connectdata *conn, bool lock)
314 {
315   struct Curl_easy *data = conn->data;
316   struct connectbundle *bundle = conn->bundle;
317   struct conncache *connc = data->state.conn_cache;
318
319   /* The bundle pointer can be NULL, since this function can be called
320      due to a failed connection attempt, before being added to a bundle */
321   if(bundle) {
322     if(lock) {
323       CONN_LOCK(conn->data);
324     }
325     bundle_remove_conn(bundle, conn);
326     if(bundle->num_connections == 0)
327       conncache_remove_bundle(connc, bundle);
328     conn->bundle = NULL; /* removed from it */
329     if(connc) {
330       connc->num_conn--;
331       DEBUGF(infof(conn->data, "The cache now contains %"
332                    CURL_FORMAT_CURL_OFF_TU " members\n",
333                    (curl_off_t) connc->num_conn));
334     }
335     if(lock) {
336       CONN_UNLOCK(conn->data);
337     }
338   }
339 }
340
341 /* This function iterates the entire connection cache and calls the function
342    func() with the connection pointer as the first argument and the supplied
343    'param' argument as the other.
344
345    The conncache lock is still held when the callback is called. It needs it,
346    so that it can safely continue traversing the lists once the callback
347    returns.
348
349    Returns 1 if the loop was aborted due to the callback's return code.
350
351    Return 0 from func() to continue the loop, return 1 to abort it.
352  */
353 bool Curl_conncache_foreach(struct Curl_easy *data,
354                             struct conncache *connc,
355                             void *param,
356                             int (*func)(struct connectdata *conn, void *param))
357 {
358   struct curl_hash_iterator iter;
359   struct curl_llist_element *curr;
360   struct curl_hash_element *he;
361
362   if(!connc)
363     return FALSE;
364
365   CONN_LOCK(data);
366   Curl_hash_start_iterate(&connc->hash, &iter);
367
368   he = Curl_hash_next_element(&iter);
369   while(he) {
370     struct connectbundle *bundle;
371
372     bundle = he->ptr;
373     he = Curl_hash_next_element(&iter);
374
375     curr = bundle->conn_list.head;
376     while(curr) {
377       /* Yes, we need to update curr before calling func(), because func()
378          might decide to remove the connection */
379       struct connectdata *conn = curr->ptr;
380       curr = curr->next;
381
382       if(1 == func(conn, param)) {
383         CONN_UNLOCK(data);
384         return TRUE;
385       }
386     }
387   }
388   CONN_UNLOCK(data);
389   return FALSE;
390 }
391
392 /* Return the first connection found in the cache. Used when closing all
393    connections.
394
395    NOTE: no locking is done here as this is presumably only done when cleaning
396    up a cache!
397 */
398 struct connectdata *
399 Curl_conncache_find_first_connection(struct conncache *connc)
400 {
401   struct curl_hash_iterator iter;
402   struct curl_hash_element *he;
403   struct connectbundle *bundle;
404
405   Curl_hash_start_iterate(&connc->hash, &iter);
406
407   he = Curl_hash_next_element(&iter);
408   while(he) {
409     struct curl_llist_element *curr;
410     bundle = he->ptr;
411
412     curr = bundle->conn_list.head;
413     if(curr) {
414       return curr->ptr;
415     }
416
417     he = Curl_hash_next_element(&iter);
418   }
419
420   return NULL;
421 }
422
423 /*
424  * Give ownership of a connection back to the connection cache. Might
425  * disconnect the oldest existing in there to make space.
426  *
427  * Return TRUE if stored, FALSE if closed.
428  */
429 bool Curl_conncache_return_conn(struct connectdata *conn)
430 {
431   struct Curl_easy *data = conn->data;
432
433   /* data->multi->maxconnects can be negative, deal with it. */
434   size_t maxconnects =
435     (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
436     data->multi->maxconnects;
437   struct connectdata *conn_candidate = NULL;
438
439   if(maxconnects > 0 &&
440      Curl_conncache_size(data) > maxconnects) {
441     infof(data, "Connection cache is full, closing the oldest one.\n");
442
443     conn_candidate = Curl_conncache_extract_oldest(data);
444
445     if(conn_candidate) {
446       /* Set the connection's owner correctly */
447       conn_candidate->data = data;
448
449       /* the winner gets the honour of being disconnected */
450       (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
451     }
452   }
453   CONN_LOCK(data);
454   conn->inuse = FALSE; /* Mark the connection unused */
455   CONN_UNLOCK(data);
456
457   return (conn_candidate == conn) ? FALSE : TRUE;
458
459 }
460
461 /*
462  * This function finds the connection in the connection bundle that has been
463  * unused for the longest time.
464  *
465  * Does not lock the connection cache!
466  *
467  * Returns the pointer to the oldest idle connection, or NULL if none was
468  * found.
469  */
470 struct connectdata *
471 Curl_conncache_extract_bundle(struct Curl_easy *data,
472                               struct connectbundle *bundle)
473 {
474   struct curl_llist_element *curr;
475   timediff_t highscore = -1;
476   timediff_t score;
477   struct curltime now;
478   struct connectdata *conn_candidate = NULL;
479   struct connectdata *conn;
480
481   (void)data;
482
483   now = Curl_now();
484
485   curr = bundle->conn_list.head;
486   while(curr) {
487     conn = curr->ptr;
488
489     if(!conn->inuse) {
490       /* Set higher score for the age passed since the connection was used */
491       score = Curl_timediff(now, conn->now);
492
493       if(score > highscore) {
494         highscore = score;
495         conn_candidate = conn;
496       }
497     }
498     curr = curr->next;
499   }
500   if(conn_candidate) {
501     /* remove it to prevent another thread from nicking it */
502     bundle_remove_conn(bundle, conn_candidate);
503     data->state.conn_cache->num_conn--;
504     DEBUGF(infof(data, "The cache now contains %"
505                  CURL_FORMAT_CURL_OFF_TU " members\n",
506                  (curl_off_t) data->state.conn_cache->num_conn));
507   }
508
509   return conn_candidate;
510 }
511
512 /*
513  * This function finds the connection in the connection cache that has been
514  * unused for the longest time and extracts that from the bundle.
515  *
516  * Returns the pointer to the connection, or NULL if none was found.
517  */
518 struct connectdata *
519 Curl_conncache_extract_oldest(struct Curl_easy *data)
520 {
521   struct conncache *connc = data->state.conn_cache;
522   struct curl_hash_iterator iter;
523   struct curl_llist_element *curr;
524   struct curl_hash_element *he;
525   timediff_t highscore =- 1;
526   timediff_t score;
527   struct curltime now;
528   struct connectdata *conn_candidate = NULL;
529   struct connectbundle *bundle;
530   struct connectbundle *bundle_candidate = NULL;
531
532   now = Curl_now();
533
534   CONN_LOCK(data);
535   Curl_hash_start_iterate(&connc->hash, &iter);
536
537   he = Curl_hash_next_element(&iter);
538   while(he) {
539     struct connectdata *conn;
540
541     bundle = he->ptr;
542
543     curr = bundle->conn_list.head;
544     while(curr) {
545       conn = curr->ptr;
546
547       if(!conn->inuse) {
548         /* Set higher score for the age passed since the connection was used */
549         score = Curl_timediff(now, conn->now);
550
551         if(score > highscore) {
552           highscore = score;
553           conn_candidate = conn;
554           bundle_candidate = bundle;
555         }
556       }
557       curr = curr->next;
558     }
559
560     he = Curl_hash_next_element(&iter);
561   }
562   if(conn_candidate) {
563     /* remove it to prevent another thread from nicking it */
564     bundle_remove_conn(bundle_candidate, conn_candidate);
565     connc->num_conn--;
566     DEBUGF(infof(data, "The cache now contains %"
567                  CURL_FORMAT_CURL_OFF_TU " members\n",
568                  (curl_off_t) connc->num_conn));
569   }
570   CONN_UNLOCK(data);
571
572   return conn_candidate;
573 }
574
575 void Curl_conncache_close_all_connections(struct conncache *connc)
576 {
577   struct connectdata *conn;
578
579   conn = Curl_conncache_find_first_connection(connc);
580   while(conn) {
581     SIGPIPE_VARIABLE(pipe_st);
582     conn->data = connc->closure_handle;
583
584     sigpipe_ignore(conn->data, &pipe_st);
585     conn->data->easy_conn = NULL; /* clear the easy handle's connection
586                                      pointer */
587     /* This will remove the connection from the cache */
588     connclose(conn, "kill all");
589     (void)Curl_disconnect(conn, FALSE);
590     sigpipe_restore(&pipe_st);
591
592     conn = Curl_conncache_find_first_connection(connc);
593   }
594
595   if(connc->closure_handle) {
596     SIGPIPE_VARIABLE(pipe_st);
597     sigpipe_ignore(connc->closure_handle, &pipe_st);
598
599     Curl_hostcache_clean(connc->closure_handle,
600                          connc->closure_handle->dns.hostcache);
601     Curl_close(connc->closure_handle);
602     sigpipe_restore(&pipe_st);
603   }
604 }
605
606 #if 0
607 /* Useful for debugging the connection cache */
608 void Curl_conncache_print(struct conncache *connc)
609 {
610   struct curl_hash_iterator iter;
611   struct curl_llist_element *curr;
612   struct curl_hash_element *he;
613
614   if(!connc)
615     return;
616
617   fprintf(stderr, "=Bundle cache=\n");
618
619   Curl_hash_start_iterate(connc->hash, &iter);
620
621   he = Curl_hash_next_element(&iter);
622   while(he) {
623     struct connectbundle *bundle;
624     struct connectdata *conn;
625
626     bundle = he->ptr;
627
628     fprintf(stderr, "%s -", he->key);
629     curr = bundle->conn_list->head;
630     while(curr) {
631       conn = curr->ptr;
632
633       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
634       curr = curr->next;
635     }
636     fprintf(stderr, "\n");
637
638     he = Curl_hash_next_element(&iter);
639   }
640 }
641 #endif