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 - 2017, 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 *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
    642