00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
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
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;
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
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
00148
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
00188
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
00252
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
00270
00271
00272
00273
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
00298
00299 struct connectdata *conn = curr->ptr;
00300 curr = curr->next;
00301
00302 if(1 == func(conn, param))
00303 return;
00304 }
00305 }
00306 }
00307
00308
00309
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
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