Index: include/curl/curl.h
===================================================================
RCS file: /cvsroot/curl/curl/include/curl/curl.h,v
retrieving revision 1.316
diff -u -r1.316 curl.h
--- include/curl/curl.h	20 Feb 2007 22:02:11 -0000	1.316
+++ include/curl/curl.h	7 Mar 2007 14:41:50 -0000
@@ -1495,6 +1495,7 @@
   CURL_LOCK_DATA_DNS,
   CURL_LOCK_DATA_SSL_SESSION,
   CURL_LOCK_DATA_CONNECT,
+  CURL_LOCK_DATA_SSL_CTX,
   CURL_LOCK_DATA_LAST
 } curl_lock_data;
 
@@ -1533,6 +1534,10 @@
   CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */
   CURLSHOPT_USERDATA,   /* pass in a user data pointer used in the lock/unlock
                            callback functions */
+  CURLSHOPT_SSL_CTX_MAX, /* pass in a non-negative integer to limit the size of
+							the cache, 0 effectively disables the cache.
+							Lowering the size when the cache is full will NOT
+							immediately trim the cache (only on cleanup) */
   CURLSHOPT_LAST  /* never use */
 } CURLSHoption;
 
Index: lib/share.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/share.c,v
retrieving revision 1.22
diff -u -r1.22 share.c
--- lib/share.c	5 Dec 2004 23:59:32 -0000	1.22
+++ lib/share.c	7 Mar 2007 14:41:51 -0000
@@ -32,6 +32,11 @@
 
 /* The last #include file should be: */
 #include "memdebug.h"
+static void ssl_ctx_cache_dtor(void* user,void* ptr)
+{
+
+}
+
 
 CURLSH *
 curl_share_init(void)
@@ -41,6 +46,7 @@
   if (share) {
     memset (share, 0, sizeof(struct Curl_share));
     share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
+    share->ssl_ctx_cache_max = 3;
   }
 
   return share;
@@ -52,6 +58,7 @@
   struct Curl_share *share = (struct Curl_share *)sh;
   va_list param;
   int type;
+  int size;
   curl_lock_function lockfunc;
   curl_unlock_function unlockfunc;
   void *ptr;
@@ -87,9 +94,17 @@
       break;
 #endif   /* CURL_DISABLE_HTTP */
 
+    case CURL_LOCK_DATA_SSL_CTX:
+      if ( !share->ssl_ctx_cache ) {
+        share->ssl_ctx_cache = Curl_llist_alloc(ssl_ctx_cache_dtor);
+        if(!share->ssl_ctx_cache)
+          return CURLSHE_NOMEM;
+      }
+      break;
     case CURL_LOCK_DATA_SSL_SESSION: /* not supported (yet) */
     case CURL_LOCK_DATA_CONNECT:     /* not supported (yet) */
 
+
     default:
       return CURLSHE_BAD_OPTION;
     }
@@ -143,6 +158,11 @@
     share->clientdata = ptr;
     break;
 
+  case CURLSHOPT_SSL_CTX_MAX:
+    size = va_arg(param, int);
+    share->ssl_ctx_cache_max = max(0,size);
+    break;
+
   default:
     return CURLSHE_BAD_OPTION;
   }
@@ -175,6 +195,8 @@
   if(share->cookies)
     Curl_cookie_cleanup(share->cookies);
 #endif   /* CURL_DISABLE_HTTP */
+  if ( share->ssl_ctx_cache )
+    Curl_ssl_ctx_cleanup(share->ssl_ctx_cache);
 
   if(share->unlockfunc)
     share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
Index: lib/share.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/share.h,v
retrieving revision 1.11
diff -u -r1.11 share.h
--- lib/share.h	26 Apr 2006 17:11:05 -0000	1.11
+++ lib/share.h	7 Mar 2007 14:41:51 -0000
@@ -47,6 +47,8 @@
 
   struct curl_hash *hostcache;
   struct CookieInfo *cookies;
+  struct curl_llist *ssl_ctx_cache;
+  size_t ssl_ctx_cache_max;
 };
 
 CURLSHcode Curl_share_lock (struct SessionHandle *, curl_lock_data,
Index: lib/sslgen.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/sslgen.c,v
retrieving revision 1.22
diff -u -r1.22 sslgen.c
--- lib/sslgen.c	26 Feb 2007 04:24:26 -0000	1.22
+++ lib/sslgen.c	7 Mar 2007 14:41:51 -0000
@@ -682,3 +682,11 @@
   return FALSE; /* nothing pending */
 
 }
+
+void Curl_ssl_ctx_cleanup(struct curl_llist* ssl_ctx_cache) {
+#ifdef USE_SSLEAY
+  Curl_ossl_ctx_cleanup(ssl_ctx_cache);
+#else
+  (void)ssl_ctx_cache;
+#endif
+}
Index: lib/sslgen.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/sslgen.h,v
retrieving revision 1.9
diff -u -r1.9 sslgen.h
--- lib/sslgen.h	25 Jan 2007 21:00:03 -0000	1.9
+++ lib/sslgen.h	7 Mar 2007 14:41:51 -0000
@@ -74,6 +74,8 @@
 bool Curl_ssl_data_pending(struct connectdata *conn,
                            int connindex);
 
+void Curl_ssl_ctx_cleanup(struct curl_llist* ssl_ctx_cache);
+
 #if !defined(USE_SSL) && !defined(SSLGEN_C)
 /* set up blank macros for none-SSL builds */
 #define Curl_ssl_close_all(x)
Index: lib/ssluse.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/ssluse.c,v
retrieving revision 1.169
diff -u -r1.169 ssluse.c
--- lib/ssluse.c	26 Feb 2007 04:24:26 -0000	1.169
+++ lib/ssluse.c	7 Mar 2007 14:41:53 -0000
@@ -50,6 +50,7 @@
 #include "strequal.h"
 #include "select.h"
 #include "sslgen.h"
+#include "share.h"
 
 #define _MPRINTF_REPLACE /* use the internal *printf() functions */
 #include <curl/mprintf.h>
@@ -112,6 +113,83 @@
 #define SSL_METHOD_QUAL
 #endif
 
+/* based on the ssl method we look for existing SSL_CTX objects. There is an
+ * underlying assumption that the following properties  are fixed across
+ * different SessionHandles:
+ * data->set.cert
+ * data->set.ssl.cipher_list
+ * data->set.ssl.CAfile || data->set.ssl.CApath
+ */
+
+static SSL_CTX* acquire_ssl_ctx(struct SessionHandle *data,
+                                SSL_METHOD_QUAL SSL_METHOD *req_method,
+                                bool* cached)
+{
+  struct Curl_share *share = data->share;
+  SSL_CTX* ctx = 0;
+
+  *cached = FALSE;
+  if ( share && share->ssl_ctx_cache && share->ssl_ctx_cache->head ) {
+    struct curl_llist_element* p;
+
+    Curl_share_lock(data, CURL_LOCK_DATA_SSL_CTX, CURL_LOCK_ACCESS_SINGLE);
+    p = share->ssl_ctx_cache->head;
+    for (; p; p = p->next)
+      if ( ((SSL_CTX*)p->ptr)->method==req_method )
+        break;
+    if ( p ) {
+      ctx = (SSL_CTX*)p->ptr;
+      Curl_llist_remove(share->ssl_ctx_cache,p,0);
+      *cached = TRUE;
+      infof(data, "using cached SSL_CTX\n");
+    }
+
+    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_CTX);
+  }
+
+  if ( !ctx )
+    infof(data, "instantiating new SSL_CTX\n");
+
+  return ctx ? ctx : SSL_CTX_new(req_method);
+}
+
+static void release_ssl_ctx(struct Curl_share *share,SSL_CTX* ctx)
+{
+
+  if ( share && share->ssl_ctx_cache && share->ssl_ctx_cache_max>0 ) {
+    if(share->lockfunc)
+      share->lockfunc(NULL, CURL_LOCK_DATA_SSL_CTX, CURL_LOCK_ACCESS_SINGLE,
+                      share->clientdata);
+
+    /* in case the cache is full, we remove the tailing (i.e. oldest)
+     * elements */
+    while ( Curl_llist_count(share->ssl_ctx_cache)>share->ssl_ctx_cache_max && share->ssl_ctx_cache->tail ) {
+      SSL_CTX* tailctx = share->ssl_ctx_cache->tail->ptr;
+      Curl_llist_remove(share->ssl_ctx_cache,share->ssl_ctx_cache->tail,0);
+      SSL_CTX_free(tailctx);
+    }
+
+    Curl_llist_insert_next(share->ssl_ctx_cache,share->ssl_ctx_cache->tail,ctx);
+    if(share->unlockfunc)
+      share->unlockfunc(NULL, CURL_LOCK_DATA_SSL_CTX, share->clientdata);
+  } else
+    SSL_CTX_free(ctx);
+}
+
+void Curl_ossl_ctx_cleanup(struct curl_llist* ssl_ctx_cache)
+{
+  if ( ssl_ctx_cache ) {
+    /* note that we can't call the lock/unlock functions, as no SessionHandle
+     * is available */
+    struct curl_llist_element* p = ssl_ctx_cache->head;
+    for (; p; p = p->next)
+      SSL_CTX_free((SSL_CTX*)p->ptr);
+
+    Curl_llist_destroy(ssl_ctx_cache,0);
+  }
+}
+
+
 /*
  * Number of bytes to read from the random number seed file. This must be
  * a finite value (because some entropy "files" like /dev/urandom have
@@ -718,7 +796,7 @@
       connssl->handle = NULL;
     }
     if(connssl->ctx) {
-      SSL_CTX_free (connssl->ctx);
+      release_ssl_ctx(conn->share,connssl->ctx);
       connssl->ctx = NULL;
     }
     connssl->use = FALSE; /* get back to ordinary socket usage */
@@ -1268,6 +1346,7 @@
   void *ssl_sessionid=NULL;
   curl_socket_t sockfd = conn->sock[sockindex];
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  bool cached;
 
   DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
 
@@ -1293,8 +1372,8 @@
   }
 
   if (connssl->ctx)
-    SSL_CTX_free(connssl->ctx);
-  connssl->ctx = SSL_CTX_new(req_method);
+    release_ssl_ctx(conn->share,connssl->ctx);
+  connssl->ctx = acquire_ssl_ctx(data,req_method,&cached);
 
   if(!connssl->ctx) {
     failf(data, "SSL: couldn't create a context!");
@@ -1336,7 +1415,7 @@
     SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
 #endif
 
-  if(data->set.cert) {
+  if(!cached && data->set.cert) {
     if(!cert_stuff(conn,
                    connssl->ctx,
                    data->set.cert,
@@ -1348,7 +1427,7 @@
     }
   }
 
-  if(data->set.ssl.cipher_list) {
+  if(!cached && data->set.ssl.cipher_list) {
     if(!SSL_CTX_set_cipher_list(connssl->ctx,
                                 data->set.ssl.cipher_list)) {
       failf(data, "failed setting cipher list");
@@ -1356,7 +1435,7 @@
     }
   }
 
-  if (data->set.ssl.CAfile || data->set.ssl.CApath) {
+  if (!cached && (data->set.ssl.CAfile || data->set.ssl.CApath)) {
     /* tell SSL where to find CA certificates that are used to verify
        the servers certificate. */
     if (!SSL_CTX_load_verify_locations(connssl->ctx, data->set.ssl.CAfile,
Index: lib/ssluse.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/ssluse.h,v
retrieving revision 1.28
diff -u -r1.28 ssluse.h
--- lib/ssluse.h	5 Jan 2007 23:11:16 -0000	1.28
+++ lib/ssluse.h	7 Mar 2007 14:41:53 -0000
@@ -66,6 +66,7 @@
 int Curl_ossl_check_cxn(struct connectdata *cxn);
 int Curl_ossl_seed(struct SessionHandle *data);
 
+void Curl_ossl_ctx_cleanup(struct curl_llist* ssl_ctx_cache);
 int Curl_ossl_shutdown(struct connectdata *conn, int sockindex);
 
 #endif
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.593
diff -u -r1.593 url.c
--- lib/url.c	27 Feb 2007 22:12:18 -0000	1.593
+++ lib/url.c	7 Mar 2007 14:41:58 -0000
@@ -4132,6 +4132,11 @@
       data->state.is_in_pipeline = TRUE;
   }
 
+  /* transfer the share handle to the connection cache, so it may be accessed
+   * after the data is freed */
+  if ( *in_connect )
+	(*in_connect)->share = data->share;
+
   return code;
 }
 
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.326
diff -u -r1.326 urldata.h
--- lib/urldata.h	25 Feb 2007 11:38:14 -0000	1.326
+++ lib/urldata.h	7 Mar 2007 14:42:01 -0000
@@ -900,6 +900,8 @@
   union {
     struct ftp_conn ftpc;
   } proto;
+
+  struct Curl_share *share; /* direct access to the share handle */
 };
 
 /* The end of connectdata. */
