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