Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus (at) haxx.se>
      9  * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel (at) 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   msnprintf(buf, len, "%ld%s", conn->port, hostname);
    179 }
    180 
    181 void Curl_conncache_unlock(struct Curl_easy *data)
    182 {
    183   CONN_UNLOCK(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 static struct connectdata *
    396 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   conn->data = NULL; /* no owner anymore */
    437   if(maxconnects > 0 &&
    438      Curl_conncache_size(data) > maxconnects) {
    439     infof(data, "Connection cache is full, closing the oldest one.\n");
    440 
    441     conn_candidate = Curl_conncache_extract_oldest(data);
    442     if(conn_candidate) {
    443       /* the winner gets the honour of being disconnected */
    444       (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
    445     }
    446   }
    447 
    448   return (conn_candidate == conn) ? FALSE : TRUE;
    449 
    450 }
    451 
    452 /*
    453  * This function finds the connection in the connection bundle that has been
    454  * unused for the longest time.
    455  *
    456  * Does not lock the connection cache!
    457  *
    458  * Returns the pointer to the oldest idle connection, or NULL if none was
    459  * found.
    460  */
    461 struct connectdata *
    462 Curl_conncache_extract_bundle(struct Curl_easy *data,
    463                               struct connectbundle *bundle)
    464 {
    465   struct curl_llist_element *curr;
    466   timediff_t highscore = -1;
    467   timediff_t score;
    468   struct curltime now;
    469   struct connectdata *conn_candidate = NULL;
    470   struct connectdata *conn;
    471 
    472   (void)data;
    473 
    474   now = Curl_now();
    475 
    476   curr = bundle->conn_list.head;
    477   while(curr) {
    478     conn = curr->ptr;
    479 
    480     if(!CONN_INUSE(conn) && !conn->data) {
    481       /* Set higher score for the age passed since the connection was used */
    482       score = Curl_timediff(now, conn->now);
    483 
    484       if(score > highscore) {
    485         highscore = score;
    486         conn_candidate = conn;
    487       }
    488     }
    489     curr = curr->next;
    490   }
    491   if(conn_candidate) {
    492     /* remove it to prevent another thread from nicking it */
    493     bundle_remove_conn(bundle, conn_candidate);
    494     data->state.conn_cache->num_conn--;
    495     DEBUGF(infof(data, "The cache now contains %zu members\n",
    496                  data->state.conn_cache->num_conn));
    497     conn_candidate->data = data; /* associate! */
    498   }
    499 
    500   return conn_candidate;
    501 }
    502 
    503 /*
    504  * This function finds the connection in the connection cache that has been
    505  * unused for the longest time and extracts that from the bundle.
    506  *
    507  * Returns the pointer to the connection, or NULL if none was found.
    508  */
    509 struct connectdata *
    510 Curl_conncache_extract_oldest(struct Curl_easy *data)
    511 {
    512   struct conncache *connc = data->state.conn_cache;
    513   struct curl_hash_iterator iter;
    514   struct curl_llist_element *curr;
    515   struct curl_hash_element *he;
    516   timediff_t highscore =- 1;
    517   timediff_t score;
    518   struct curltime now;
    519   struct connectdata *conn_candidate = NULL;
    520   struct connectbundle *bundle;
    521   struct connectbundle *bundle_candidate = NULL;
    522 
    523   now = Curl_now();
    524 
    525   CONN_LOCK(data);
    526   Curl_hash_start_iterate(&connc->hash, &iter);
    527 
    528   he = Curl_hash_next_element(&iter);
    529   while(he) {
    530     struct connectdata *conn;
    531 
    532     bundle = he->ptr;
    533 
    534     curr = bundle->conn_list.head;
    535     while(curr) {
    536       conn = curr->ptr;
    537 
    538       if(!CONN_INUSE(conn) && !conn->data) {
    539         /* Set higher score for the age passed since the connection was used */
    540         score = Curl_timediff(now, conn->now);
    541 
    542         if(score > highscore) {
    543           highscore = score;
    544           conn_candidate = conn;
    545           bundle_candidate = bundle;
    546         }
    547       }
    548       curr = curr->next;
    549     }
    550 
    551     he = Curl_hash_next_element(&iter);
    552   }
    553   if(conn_candidate) {
    554     /* remove it to prevent another thread from nicking it */
    555     bundle_remove_conn(bundle_candidate, conn_candidate);
    556     connc->num_conn--;
    557     DEBUGF(infof(data, "The cache now contains %zu members\n",
    558                  connc->num_conn));
    559     conn_candidate->data = data; /* associate! */
    560   }
    561   CONN_UNLOCK(data);
    562 
    563   return conn_candidate;
    564 }
    565 
    566 void Curl_conncache_close_all_connections(struct conncache *connc)
    567 {
    568   struct connectdata *conn;
    569 
    570   conn = conncache_find_first_connection(connc);
    571   while(conn) {
    572     SIGPIPE_VARIABLE(pipe_st);
    573     conn->data = connc->closure_handle;
    574 
    575     sigpipe_ignore(conn->data, &pipe_st);
    576     /* This will remove the connection from the cache */
    577     connclose(conn, "kill all");
    578     (void)Curl_disconnect(connc->closure_handle, conn, FALSE);
    579     sigpipe_restore(&pipe_st);
    580 
    581     conn = conncache_find_first_connection(connc);
    582   }
    583 
    584   if(connc->closure_handle) {
    585     SIGPIPE_VARIABLE(pipe_st);
    586     sigpipe_ignore(connc->closure_handle, &pipe_st);
    587 
    588     Curl_hostcache_clean(connc->closure_handle,
    589                          connc->closure_handle->dns.hostcache);
    590     Curl_close(connc->closure_handle);
    591     sigpipe_restore(&pipe_st);
    592   }
    593 }
    594 
    595 #if 0
    596 /* Useful for debugging the connection cache */
    597 void Curl_conncache_print(struct conncache *connc)
    598 {
    599   struct curl_hash_iterator iter;
    600   struct curl_llist_element *curr;
    601   struct curl_hash_element *he;
    602 
    603   if(!connc)
    604     return;
    605 
    606   fprintf(stderr, "=Bundle cache=\n");
    607 
    608   Curl_hash_start_iterate(connc->hash, &iter);
    609 
    610   he = Curl_hash_next_element(&iter);
    611   while(he) {
    612     struct connectbundle *bundle;
    613     struct connectdata *conn;
    614 
    615     bundle = he->ptr;
    616 
    617     fprintf(stderr, "%s -", he->key);
    618     curr = bundle->conn_list->head;
    619     while(curr) {
    620       conn = curr->ptr;
    621 
    622       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
    623       curr = curr->next;
    624     }
    625     fprintf(stderr, "\n");
    626 
    627     he = Curl_hash_next_element(&iter);
    628   }
    629 }
    630 #endif
    631