conncache.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 2012, 2016, Linus Nielsen Feltzing, <linus@haxx.se>
00009  * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
00010  *
00011  * This software is licensed as described in the file COPYING, which
00012  * you should have received as part of this distribution. The terms
00013  * are also available at https://curl.haxx.se/docs/copyright.html.
00014  *
00015  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00016  * copies of the Software, and permit persons to whom the Software is
00017  * furnished to do so, under the terms of the COPYING file.
00018  *
00019  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00020  * KIND, either express or implied.
00021  *
00022  ***************************************************************************/
00023 
00024 #include "curl_setup.h"
00025 
00026 #include <curl/curl.h>
00027 
00028 #include "urldata.h"
00029 #include "url.h"
00030 #include "progress.h"
00031 #include "multiif.h"
00032 #include "sendf.h"
00033 #include "conncache.h"
00034 /* The last 3 #include files should be in this order */
00035 #include "curl_printf.h"
00036 #include "curl_memory.h"
00037 #include "memdebug.h"
00038 
00039 static void conn_llist_dtor(void *user, void *element)
00040 {
00041   struct connectdata *data = element;
00042   (void)user;
00043 
00044   data->bundle = NULL;
00045 }
00046 
00047 static CURLcode bundle_create(struct Curl_easy *data,
00048                               struct connectbundle **cb_ptr)
00049 {
00050   (void)data;
00051   DEBUGASSERT(*cb_ptr == NULL);
00052   *cb_ptr = malloc(sizeof(struct connectbundle));
00053   if(!*cb_ptr)
00054     return CURLE_OUT_OF_MEMORY;
00055 
00056   (*cb_ptr)->num_connections = 0;
00057   (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
00058 
00059   (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor);
00060   if(!(*cb_ptr)->conn_list) {
00061     Curl_safefree(*cb_ptr);
00062     return CURLE_OUT_OF_MEMORY;
00063   }
00064   return CURLE_OK;
00065 }
00066 
00067 static void bundle_destroy(struct connectbundle *cb_ptr)
00068 {
00069   if(!cb_ptr)
00070     return;
00071 
00072   if(cb_ptr->conn_list) {
00073     Curl_llist_destroy(cb_ptr->conn_list, NULL);
00074     cb_ptr->conn_list = NULL;
00075   }
00076   free(cb_ptr);
00077 }
00078 
00079 /* Add a connection to a bundle */
00080 static CURLcode bundle_add_conn(struct connectbundle *cb_ptr,
00081                               struct connectdata *conn)
00082 {
00083   if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
00084     return CURLE_OUT_OF_MEMORY;
00085 
00086   conn->bundle = cb_ptr;
00087 
00088   cb_ptr->num_connections++;
00089   return CURLE_OK;
00090 }
00091 
00092 /* Remove a connection from a bundle */
00093 static int bundle_remove_conn(struct connectbundle *cb_ptr,
00094                               struct connectdata *conn)
00095 {
00096   struct curl_llist_element *curr;
00097 
00098   curr = cb_ptr->conn_list->head;
00099   while(curr) {
00100     if(curr->ptr == conn) {
00101       Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
00102       cb_ptr->num_connections--;
00103       conn->bundle = NULL;
00104       return 1; /* we removed a handle */
00105     }
00106     curr = curr->next;
00107   }
00108   return 0;
00109 }
00110 
00111 static void free_bundle_hash_entry(void *freethis)
00112 {
00113   struct connectbundle *b = (struct connectbundle *) freethis;
00114 
00115   bundle_destroy(b);
00116 }
00117 
00118 int Curl_conncache_init(struct conncache *connc, int size)
00119 {
00120   return Curl_hash_init(&connc->hash, size, Curl_hash_str,
00121                         Curl_str_key_compare, free_bundle_hash_entry);
00122 }
00123 
00124 void Curl_conncache_destroy(struct conncache *connc)
00125 {
00126   if(connc)
00127     Curl_hash_destroy(&connc->hash);
00128 }
00129 
00130 /* returns an allocated key to find a bundle for this connection */
00131 static char *hashkey(struct connectdata *conn)
00132 {
00133   const char *hostname;
00134 
00135   if(conn->bits.socksproxy)
00136     hostname = conn->socks_proxy.host.name;
00137   else if(conn->bits.httpproxy)
00138     hostname = conn->http_proxy.host.name;
00139   else if(conn->bits.conn_to_host)
00140     hostname = conn->conn_to_host.name;
00141   else
00142     hostname = conn->host.name;
00143 
00144   return aprintf("%s:%d", hostname, conn->port);
00145 }
00146 
00147 /* Look up the bundle with all the connections to the same host this
00148    connectdata struct is setup to use. */
00149 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
00150                                                  struct conncache *connc)
00151 {
00152   struct connectbundle *bundle = NULL;
00153   if(connc) {
00154     char *key = hashkey(conn);
00155     if(key) {
00156       bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
00157       free(key);
00158     }
00159   }
00160 
00161   return bundle;
00162 }
00163 
00164 static bool conncache_add_bundle(struct conncache *connc,
00165                                  char *key,
00166                                  struct connectbundle *bundle)
00167 {
00168   void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
00169 
00170   return p?TRUE:FALSE;
00171 }
00172 
00173 static void conncache_remove_bundle(struct conncache *connc,
00174                                     struct connectbundle *bundle)
00175 {
00176   struct curl_hash_iterator iter;
00177   struct curl_hash_element *he;
00178 
00179   if(!connc)
00180     return;
00181 
00182   Curl_hash_start_iterate(&connc->hash, &iter);
00183 
00184   he = Curl_hash_next_element(&iter);
00185   while(he) {
00186     if(he->ptr == bundle) {
00187       /* The bundle is destroyed by the hash destructor function,
00188          free_bundle_hash_entry() */
00189       Curl_hash_delete(&connc->hash, he->key, he->key_len);
00190       return;
00191     }
00192 
00193     he = Curl_hash_next_element(&iter);
00194   }
00195 }
00196 
00197 CURLcode Curl_conncache_add_conn(struct conncache *connc,
00198                                  struct connectdata *conn)
00199 {
00200   CURLcode result;
00201   struct connectbundle *bundle;
00202   struct connectbundle *new_bundle = NULL;
00203   struct Curl_easy *data = conn->data;
00204 
00205   bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
00206   if(!bundle) {
00207     char *key;
00208     int rc;
00209 
00210     result = bundle_create(data, &new_bundle);
00211     if(result)
00212       return result;
00213 
00214     key = hashkey(conn);
00215     if(!key) {
00216       bundle_destroy(new_bundle);
00217       return CURLE_OUT_OF_MEMORY;
00218     }
00219 
00220     rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
00221     free(key);
00222     if(!rc) {
00223       bundle_destroy(new_bundle);
00224       return CURLE_OUT_OF_MEMORY;
00225     }
00226     bundle = new_bundle;
00227   }
00228 
00229   result = bundle_add_conn(bundle, conn);
00230   if(result) {
00231     if(new_bundle)
00232       conncache_remove_bundle(data->state.conn_cache, new_bundle);
00233     return result;
00234   }
00235 
00236   conn->connection_id = connc->next_connection_id++;
00237   connc->num_connections++;
00238 
00239   DEBUGF(infof(conn->data, "Added connection %ld. "
00240                "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
00241                conn->connection_id, (curl_off_t) connc->num_connections));
00242 
00243   return CURLE_OK;
00244 }
00245 
00246 void Curl_conncache_remove_conn(struct conncache *connc,
00247                                 struct connectdata *conn)
00248 {
00249   struct connectbundle *bundle = conn->bundle;
00250 
00251   /* The bundle pointer can be NULL, since this function can be called
00252      due to a failed connection attempt, before being added to a bundle */
00253   if(bundle) {
00254     bundle_remove_conn(bundle, conn);
00255     if(bundle->num_connections == 0) {
00256       conncache_remove_bundle(connc, bundle);
00257     }
00258 
00259     if(connc) {
00260       connc->num_connections--;
00261 
00262       DEBUGF(infof(conn->data, "The cache now contains %"
00263                    CURL_FORMAT_CURL_OFF_TU " members\n",
00264                    (curl_off_t) connc->num_connections));
00265     }
00266   }
00267 }
00268 
00269 /* This function iterates the entire connection cache and calls the
00270    function func() with the connection pointer as the first argument
00271    and the supplied 'param' argument as the other,
00272 
00273    Return 0 from func() to continue the loop, return 1 to abort it.
00274  */
00275 void Curl_conncache_foreach(struct conncache *connc,
00276                             void *param,
00277                             int (*func)(struct connectdata *conn, void *param))
00278 {
00279   struct curl_hash_iterator iter;
00280   struct curl_llist_element *curr;
00281   struct curl_hash_element *he;
00282 
00283   if(!connc)
00284     return;
00285 
00286   Curl_hash_start_iterate(&connc->hash, &iter);
00287 
00288   he = Curl_hash_next_element(&iter);
00289   while(he) {
00290     struct connectbundle *bundle;
00291 
00292     bundle = he->ptr;
00293     he = Curl_hash_next_element(&iter);
00294 
00295     curr = bundle->conn_list->head;
00296     while(curr) {
00297       /* Yes, we need to update curr before calling func(), because func()
00298          might decide to remove the connection */
00299       struct connectdata *conn = curr->ptr;
00300       curr = curr->next;
00301 
00302       if(1 == func(conn, param))
00303         return;
00304     }
00305   }
00306 }
00307 
00308 /* Return the first connection found in the cache. Used when closing all
00309    connections */
00310 struct connectdata *
00311 Curl_conncache_find_first_connection(struct conncache *connc)
00312 {
00313   struct curl_hash_iterator iter;
00314   struct curl_hash_element *he;
00315   struct connectbundle *bundle;
00316 
00317   Curl_hash_start_iterate(&connc->hash, &iter);
00318 
00319   he = Curl_hash_next_element(&iter);
00320   while(he) {
00321     struct curl_llist_element *curr;
00322     bundle = he->ptr;
00323 
00324     curr = bundle->conn_list->head;
00325     if(curr) {
00326       return curr->ptr;
00327     }
00328 
00329     he = Curl_hash_next_element(&iter);
00330   }
00331 
00332   return NULL;
00333 }
00334 
00335 
00336 #if 0
00337 /* Useful for debugging the connection cache */
00338 void Curl_conncache_print(struct conncache *connc)
00339 {
00340   struct curl_hash_iterator iter;
00341   struct curl_llist_element *curr;
00342   struct curl_hash_element *he;
00343 
00344   if(!connc)
00345     return;
00346 
00347   fprintf(stderr, "=Bundle cache=\n");
00348 
00349   Curl_hash_start_iterate(connc->hash, &iter);
00350 
00351   he = Curl_hash_next_element(&iter);
00352   while(he) {
00353     struct connectbundle *bundle;
00354     struct connectdata *conn;
00355 
00356     bundle = he->ptr;
00357 
00358     fprintf(stderr, "%s -", he->key);
00359     curr = bundle->conn_list->head;
00360     while(curr) {
00361       conn = curr->ptr;
00362 
00363       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
00364       curr = curr->next;
00365     }
00366     fprintf(stderr, "\n");
00367 
00368     he = Curl_hash_next_element(&iter);
00369   }
00370 }
00371 #endif


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02