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 - 2015, 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 /* The last 3 #include files should be in this order */
     35 #include "curl_printf.h"
     36 #include "curl_memory.h"
     37 #include "memdebug.h"
     38 
     39 static void conn_llist_dtor(void *user, void *element)
     40 {
     41   struct connectdata *data = element;
     42   (void)user;
     43 
     44   data->bundle = NULL;
     45 }
     46 
     47 static CURLcode bundle_create(struct Curl_easy *data,
     48                               struct connectbundle **cb_ptr)
     49 {
     50   (void)data;
     51   DEBUGASSERT(*cb_ptr == NULL);
     52   *cb_ptr = malloc(sizeof(struct connectbundle));
     53   if(!*cb_ptr)
     54     return CURLE_OUT_OF_MEMORY;
     55 
     56   (*cb_ptr)->num_connections = 0;
     57   (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
     58 
     59   (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor);
     60   if(!(*cb_ptr)->conn_list) {
     61     Curl_safefree(*cb_ptr);
     62     return CURLE_OUT_OF_MEMORY;
     63   }
     64   return CURLE_OK;
     65 }
     66 
     67 static void bundle_destroy(struct connectbundle *cb_ptr)
     68 {
     69   if(!cb_ptr)
     70     return;
     71 
     72   if(cb_ptr->conn_list) {
     73     Curl_llist_destroy(cb_ptr->conn_list, NULL);
     74     cb_ptr->conn_list = NULL;
     75   }
     76   free(cb_ptr);
     77 }
     78 
     79 /* Add a connection to a bundle */
     80 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
     81                               struct connectdata *conn)
     82 {
     83   if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
     84     return CURLE_OUT_OF_MEMORY;
     85 
     86   conn->bundle = cb_ptr;
     87 
     88   cb_ptr->num_connections++;
     89   return CURLE_OK;
     90 }
     91 
     92 /* Remove a connection from a bundle */
     93 static int bundle_remove_conn(struct connectbundle *cb_ptr,
     94                               struct connectdata *conn)
     95 {
     96   struct curl_llist_element *curr;
     97 
     98   curr = cb_ptr->conn_list->head;
     99   while(curr) {
    100     if(curr->ptr == conn) {
    101       Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
    102       cb_ptr->num_connections--;
    103       conn->bundle = NULL;
    104       return 1; /* we removed a handle */
    105     }
    106     curr = curr->next;
    107   }
    108   return 0;
    109 }
    110 
    111 static void free_bundle_hash_entry(void *freethis)
    112 {
    113   struct connectbundle *b = (struct connectbundle *) freethis;
    114 
    115   bundle_destroy(b);
    116 }
    117 
    118 int Curl_conncache_init(struct conncache *connc, int size)
    119 {
    120   return Curl_hash_init(&connc->hash, size, Curl_hash_str,
    121                         Curl_str_key_compare, free_bundle_hash_entry);
    122 }
    123 
    124 void Curl_conncache_destroy(struct conncache *connc)
    125 {
    126   if(connc)
    127     Curl_hash_destroy(&connc->hash);
    128 }
    129 
    130 /* returns an allocated key to find a bundle for this connection */
    131 static char *hashkey(struct connectdata *conn)
    132 {
    133   const char *hostname;
    134 
    135   if(conn->bits.socksproxy)
    136     hostname = conn->socks_proxy.host.name;
    137   else if(conn->bits.httpproxy)
    138     hostname = conn->http_proxy.host.name;
    139   else if(conn->bits.conn_to_host)
    140     hostname = conn->conn_to_host.name;
    141   else
    142     hostname = conn->host.name;
    143 
    144   return aprintf("%s:%d", hostname, conn->port);
    145 }
    146 
    147 /* Look up the bundle with all the connections to the same host this
    148    connectdata struct is setup to use. */
    149 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
    150                                                  struct conncache *connc)
    151 {
    152   struct connectbundle *bundle = NULL;
    153   if(connc) {
    154     char *key = hashkey(conn);
    155     if(key) {
    156       bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
    157       free(key);
    158     }
    159   }
    160 
    161   return bundle;
    162 }
    163 
    164 static bool conncache_add_bundle(struct conncache *connc,
    165                                  char *key,
    166                                  struct connectbundle *bundle)
    167 {
    168   void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
    169 
    170   return p?TRUE:FALSE;
    171 }
    172 
    173 static void conncache_remove_bundle(struct conncache *connc,
    174                                     struct connectbundle *bundle)
    175 {
    176   struct curl_hash_iterator iter;
    177   struct curl_hash_element *he;
    178 
    179   if(!connc)
    180     return;
    181 
    182   Curl_hash_start_iterate(&connc->hash, &iter);
    183 
    184   he = Curl_hash_next_element(&iter);
    185   while(he) {
    186     if(he->ptr == bundle) {
    187       /* The bundle is destroyed by the hash destructor function,
    188          free_bundle_hash_entry() */
    189       Curl_hash_delete(&connc->hash, he->key, he->key_len);
    190       return;
    191     }
    192 
    193     he = Curl_hash_next_element(&iter);
    194   }
    195 }
    196 
    197 CURLcode Curl_conncache_add_conn(struct conncache *connc,
    198                                  struct connectdata *conn)
    199 {
    200   CURLcode result;
    201   struct connectbundle *bundle;
    202   struct connectbundle *new_bundle = NULL;
    203   struct Curl_easy *data = conn->data;
    204 
    205   bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
    206   if(!bundle) {
    207     char *key;
    208     int rc;
    209 
    210     result = bundle_create(data, &new_bundle);
    211     if(result)
    212       return result;
    213 
    214     key = hashkey(conn);
    215     if(!key) {
    216       bundle_destroy(new_bundle);
    217       return CURLE_OUT_OF_MEMORY;
    218     }
    219 
    220     rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
    221     free(key);
    222     if(!rc) {
    223       bundle_destroy(new_bundle);
    224       return CURLE_OUT_OF_MEMORY;
    225     }
    226     bundle = new_bundle;
    227   }
    228 
    229   result = bundle_add_conn(bundle, conn);
    230   if(result) {
    231     if(new_bundle)
    232       conncache_remove_bundle(data->state.conn_cache, new_bundle);
    233     return result;
    234   }
    235 
    236   conn->connection_id = connc->next_connection_id++;
    237   connc->num_connections++;
    238 
    239   DEBUGF(infof(conn->data, "Added connection %ld. "
    240                "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
    241                conn->connection_id, (curl_off_t) connc->num_connections));
    242 
    243   return CURLE_OK;
    244 }
    245 
    246 void Curl_conncache_remove_conn(struct conncache *connc,
    247                                 struct connectdata *conn)
    248 {
    249   struct connectbundle *bundle = conn->bundle;
    250 
    251   /* The bundle pointer can be NULL, since this function can be called
    252      due to a failed connection attempt, before being added to a bundle */
    253   if(bundle) {
    254     bundle_remove_conn(bundle, conn);
    255     if(bundle->num_connections == 0) {
    256       conncache_remove_bundle(connc, bundle);
    257     }
    258 
    259     if(connc) {
    260       connc->num_connections--;
    261 
    262       DEBUGF(infof(conn->data, "The cache now contains %"
    263                    CURL_FORMAT_CURL_OFF_TU " members\n",
    264                    (curl_off_t) connc->num_connections));
    265     }
    266   }
    267 }
    268 
    269 /* This function iterates the entire connection cache and calls the
    270    function func() with the connection pointer as the first argument
    271    and the supplied 'param' argument as the other,
    272 
    273    Return 0 from func() to continue the loop, return 1 to abort it.
    274  */
    275 void Curl_conncache_foreach(struct conncache *connc,
    276                             void *param,
    277                             int (*func)(struct connectdata *conn, void *param))
    278 {
    279   struct curl_hash_iterator iter;
    280   struct curl_llist_element *curr;
    281   struct curl_hash_element *he;
    282 
    283   if(!connc)
    284     return;
    285 
    286   Curl_hash_start_iterate(&connc->hash, &iter);
    287 
    288   he = Curl_hash_next_element(&iter);
    289   while(he) {
    290     struct connectbundle *bundle;
    291 
    292     bundle = he->ptr;
    293     he = Curl_hash_next_element(&iter);
    294 
    295     curr = bundle->conn_list->head;
    296     while(curr) {
    297       /* Yes, we need to update curr before calling func(), because func()
    298          might decide to remove the connection */
    299       struct connectdata *conn = curr->ptr;
    300       curr = curr->next;
    301 
    302       if(1 == func(conn, param))
    303         return;
    304     }
    305   }
    306 }
    307 
    308 /* Return the first connection found in the cache. Used when closing all
    309    connections */
    310 struct connectdata *
    311 Curl_conncache_find_first_connection(struct conncache *connc)
    312 {
    313   struct curl_hash_iterator iter;
    314   struct curl_hash_element *he;
    315   struct connectbundle *bundle;
    316 
    317   Curl_hash_start_iterate(&connc->hash, &iter);
    318 
    319   he = Curl_hash_next_element(&iter);
    320   while(he) {
    321     struct curl_llist_element *curr;
    322     bundle = he->ptr;
    323 
    324     curr = bundle->conn_list->head;
    325     if(curr) {
    326       return curr->ptr;
    327     }
    328 
    329     he = Curl_hash_next_element(&iter);
    330   }
    331 
    332   return NULL;
    333 }
    334 
    335 
    336 #if 0
    337 /* Useful for debugging the connection cache */
    338 void Curl_conncache_print(struct conncache *connc)
    339 {
    340   struct curl_hash_iterator iter;
    341   struct curl_llist_element *curr;
    342   struct curl_hash_element *he;
    343 
    344   if(!connc)
    345     return;
    346 
    347   fprintf(stderr, "=Bundle cache=\n");
    348 
    349   Curl_hash_start_iterate(connc->hash, &iter);
    350 
    351   he = Curl_hash_next_element(&iter);
    352   while(he) {
    353     struct connectbundle *bundle;
    354     struct connectdata *conn;
    355 
    356     bundle = he->ptr;
    357 
    358     fprintf(stderr, "%s -", he->key);
    359     curr = bundle->conn_list->head;
    360     while(curr) {
    361       conn = curr->ptr;
    362 
    363       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
    364       curr = curr->next;
    365     }
    366     fprintf(stderr, "\n");
    367 
    368     he = Curl_hash_next_element(&iter);
    369   }
    370 }
    371 #endif
    372