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