#include "curl_setup.h"
#include <curl/curl.h>
#include "urldata.h"
#include "url.h"
#include "progress.h"
#include "multiif.h"
#include "sendf.h"
#include "rawstr.h"
#include "bundles.h"
#include "conncache.h"
#include "curl_memory.h"
#include "memdebug.h"
#define CONNECTION_HASH_SIZE 97
static void free_bundle_hash_entry(void *freethis)
{
struct connectbundle *b = (struct connectbundle *) freethis;
Curl_bundle_destroy(b);
}
struct conncache *Curl_conncache_init(void)
{
struct conncache *connc;
connc = calloc(1, sizeof(struct conncache));
if(!connc)
return NULL;
connc->hash = Curl_hash_alloc(CONNECTION_HASH_SIZE, Curl_hash_str,
Curl_str_key_compare, free_bundle_hash_entry);
if(!connc->hash) {
free(connc);
return NULL;
}
return connc;
}
void Curl_conncache_destroy(struct conncache *connc)
{
if(connc) {
Curl_hash_destroy(connc->hash);
connc->hash = NULL;
free(connc);
}
}
struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
char *hostname)
{
struct connectbundle *bundle = NULL;
if(connc)
bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
return bundle;
}
static bool conncache_add_bundle(struct conncache *connc,
char *hostname,
struct connectbundle *bundle)
{
void *p;
p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
return p?TRUE:FALSE;
}
static void conncache_remove_bundle(struct conncache *connc,
struct connectbundle *bundle)
{
struct curl_hash_iterator iter;
struct curl_hash_element *he;
if(!connc)
return;
Curl_hash_start_iterate(connc->hash, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
if(he->ptr == bundle) {
Curl_hash_delete(connc->hash, he->key, he->key_len);
return;
}
he = Curl_hash_next_element(&iter);
}
}
CURLcode Curl_conncache_add_conn(struct conncache *connc,
struct connectdata *conn)
{
CURLcode result;
struct connectbundle *bundle;
struct connectbundle *new_bundle = NULL;
struct SessionHandle *data = conn->data;
bundle = Curl_conncache_find_bundle(data->state.conn_cache,
conn->host.name);
if(!bundle) {
result = Curl_bundle_create(data, &new_bundle);
if(result != CURLE_OK)
return result;
if(!conncache_add_bundle(data->state.conn_cache,
conn->host.name, new_bundle)) {
Curl_bundle_destroy(new_bundle);
return CURLE_OUT_OF_MEMORY;
}
bundle = new_bundle;
}
result = Curl_bundle_add_conn(bundle, conn);
if(result != CURLE_OK) {
if(new_bundle)
conncache_remove_bundle(data->state.conn_cache, new_bundle);
return result;
}
connc->num_connections++;
return CURLE_OK;
}
void Curl_conncache_remove_conn(struct conncache *connc,
struct connectdata *conn)
{
struct connectbundle *bundle = conn->bundle;
if(bundle) {
Curl_bundle_remove_conn(bundle, conn);
if(bundle->num_connections == 0) {
conncache_remove_bundle(connc, bundle);
}
connc->num_connections--;
DEBUGF(infof(conn->data, "The cache now contains %d members\n",
connc->num_connections));
}
}
void Curl_conncache_foreach(struct conncache *connc,
void *param,
int (*func)(struct connectdata *conn, void *param))
{
struct curl_hash_iterator iter;
struct curl_llist_element *curr;
struct curl_hash_element *he;
if(!connc)
return;
Curl_hash_start_iterate(connc->hash, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
struct connectbundle *bundle;
struct connectdata *conn;
bundle = he->ptr;
curr = bundle->conn_list->head;
while(curr) {
conn = curr->ptr;
curr = curr->next;
if(1 == func(conn, param))
return;
}
he = Curl_hash_next_element(&iter);
}
}
struct connectdata *
Curl_conncache_find_first_connection(struct conncache *connc)
{
struct curl_hash_iterator iter;
struct curl_llist_element *curr;
struct curl_hash_element *he;
struct connectbundle *bundle;
Curl_hash_start_iterate(connc->hash, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
bundle = he->ptr;
curr = bundle->conn_list->head;
if(curr) {
return curr->ptr;
}
he = Curl_hash_next_element(&iter);
}
return NULL;
}
#if 0
void Curl_conncache_print(struct conncache *connc)
{
struct curl_hash_iterator iter;
struct curl_llist_element *curr;
struct curl_hash_element *he;
if(!connc)
return;
fprintf(stderr, "=Bundle cache=\n");
Curl_hash_start_iterate(connc->hash, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
struct connectbundle *bundle;
struct connectdata *conn;
bundle = he->ptr;
fprintf(stderr, "%s -", he->key);
curr = bundle->conn_list->head;
while(curr) {
conn = curr->ptr;
fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
curr = curr->next;
}
fprintf(stderr, "\n");
he = Curl_hash_next_element(&iter);
}
}
#endif