1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se> 9 * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@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 "bundles.h" 35#include "conncache.h" 36 37#include "curl_memory.h" 38/* The last #include file should be: */ 39#include "memdebug.h" 40 41static void free_bundle_hash_entry(void *freethis) 42{ 43 struct connectbundle *b = (struct connectbundle *) freethis; 44 45 Curl_bundle_destroy(b); 46} 47 48struct conncache *Curl_conncache_init(int size) 49{ 50 struct conncache *connc; 51 52 connc = calloc(1, sizeof(struct conncache)); 53 if(!connc) 54 return NULL; 55 56 connc->hash = Curl_hash_alloc(size, Curl_hash_str, 57 Curl_str_key_compare, free_bundle_hash_entry); 58 59 if(!connc->hash) { 60 free(connc); 61 return NULL; 62 } 63 64 return connc; 65} 66 67void Curl_conncache_destroy(struct conncache *connc) 68{ 69 if(connc) { 70 Curl_hash_destroy(connc->hash); 71 connc->hash = NULL; 72 free(connc); 73 } 74} 75 76struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc, 77 char *hostname) 78{ 79 struct connectbundle *bundle = NULL; 80 81 if(connc) 82 bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1); 83 84 return bundle; 85} 86 87static bool conncache_add_bundle(struct conncache *connc, 88 char *hostname, 89 struct connectbundle *bundle) 90{ 91 void *p; 92 93 p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle); 94 95 return p?TRUE:FALSE; 96} 97 98static void conncache_remove_bundle(struct conncache *connc, 99 struct connectbundle *bundle) 100{ 101 struct curl_hash_iterator iter; 102 struct curl_hash_element *he; 103 104 if(!connc) 105 return; 106 107 Curl_hash_start_iterate(connc->hash, &iter); 108 109 he = Curl_hash_next_element(&iter); 110 while(he) { 111 if(he->ptr == bundle) { 112 /* The bundle is destroyed by the hash destructor function, 113 free_bundle_hash_entry() */ 114 Curl_hash_delete(connc->hash, he->key, he->key_len); 115 return; 116 } 117 118 he = Curl_hash_next_element(&iter); 119 } 120} 121 122CURLcode Curl_conncache_add_conn(struct conncache *connc, 123 struct connectdata *conn) 124{ 125 CURLcode result; 126 struct connectbundle *bundle; 127 struct connectbundle *new_bundle = NULL; 128 struct SessionHandle *data = conn->data; 129 130 bundle = Curl_conncache_find_bundle(data->state.conn_cache, 131 conn->host.name); 132 if(!bundle) { 133 result = Curl_bundle_create(data, &new_bundle); 134 if(result != CURLE_OK) 135 return result; 136 137 if(!conncache_add_bundle(data->state.conn_cache, 138 conn->host.name, new_bundle)) { 139 Curl_bundle_destroy(new_bundle); 140 return CURLE_OUT_OF_MEMORY; 141 } 142 bundle = new_bundle; 143 } 144 145 result = Curl_bundle_add_conn(bundle, conn); 146 if(result != CURLE_OK) { 147 if(new_bundle) 148 conncache_remove_bundle(data->state.conn_cache, new_bundle); 149 return result; 150 } 151 152 conn->connection_id = connc->next_connection_id++; 153 connc->num_connections++; 154 155 return CURLE_OK; 156} 157 158void Curl_conncache_remove_conn(struct conncache *connc, 159 struct connectdata *conn) 160{ 161 struct connectbundle *bundle = conn->bundle; 162 163 /* The bundle pointer can be NULL, since this function can be called 164 due to a failed connection attempt, before being added to a bundle */ 165 if(bundle) { 166 Curl_bundle_remove_conn(bundle, conn); 167 if(bundle->num_connections == 0) { 168 conncache_remove_bundle(connc, bundle); 169 } 170 171 if(connc) { 172 connc->num_connections--; 173 174 DEBUGF(infof(conn->data, "The cache now contains %d members\n", 175 connc->num_connections)); 176 } 177 } 178} 179 180/* This function iterates the entire connection cache and calls the 181 function func() with the connection pointer as the first argument 182 and the supplied 'param' argument as the other, 183 184 Return 0 from func() to continue the loop, return 1 to abort it. 185 */ 186void Curl_conncache_foreach(struct conncache *connc, 187 void *param, 188 int (*func)(struct connectdata *conn, void *param)) 189{ 190 struct curl_hash_iterator iter; 191 struct curl_llist_element *curr; 192 struct curl_hash_element *he; 193 194 if(!connc) 195 return; 196 197 Curl_hash_start_iterate(connc->hash, &iter); 198 199 he = Curl_hash_next_element(&iter); 200 while(he) { 201 struct connectbundle *bundle; 202 struct connectdata *conn; 203 204 bundle = he->ptr; 205 206 curr = bundle->conn_list->head; 207 while(curr) { 208 /* Yes, we need to update curr before calling func(), because func() 209 might decide to remove the connection */ 210 conn = curr->ptr; 211 curr = curr->next; 212 213 if(1 == func(conn, param)) 214 return; 215 } 216 217 he = Curl_hash_next_element(&iter); 218 } 219} 220 221/* Return the first connection found in the cache. Used when closing all 222 connections */ 223struct connectdata * 224Curl_conncache_find_first_connection(struct conncache *connc) 225{ 226 struct curl_hash_iterator iter; 227 struct curl_llist_element *curr; 228 struct curl_hash_element *he; 229 struct connectbundle *bundle; 230 231 Curl_hash_start_iterate(connc->hash, &iter); 232 233 he = Curl_hash_next_element(&iter); 234 while(he) { 235 bundle = he->ptr; 236 237 curr = bundle->conn_list->head; 238 if(curr) { 239 return curr->ptr; 240 } 241 242 he = Curl_hash_next_element(&iter); 243 } 244 245 return NULL; 246} 247 248 249#if 0 250/* Useful for debugging the connection cache */ 251void Curl_conncache_print(struct conncache *connc) 252{ 253 struct curl_hash_iterator iter; 254 struct curl_llist_element *curr; 255 struct curl_hash_element *he; 256 257 if(!connc) 258 return; 259 260 fprintf(stderr, "=Bundle cache=\n"); 261 262 Curl_hash_start_iterate(connc->hash, &iter); 263 264 he = Curl_hash_next_element(&iter); 265 while(he) { 266 struct connectbundle *bundle; 267 struct connectdata *conn; 268 269 bundle = he->ptr; 270 271 fprintf(stderr, "%s -", he->key); 272 curr = bundle->conn_list->head; 273 while(curr) { 274 conn = curr->ptr; 275 276 fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); 277 curr = curr->next; 278 } 279 fprintf(stderr, "\n"); 280 281 he = Curl_hash_next_element(&iter); 282 } 283} 284#endif 285