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