SSL session sharing support added
With locking, plus test, plus documentation
This commit is contained in:
parent
ff5ba6e43d
commit
5793bc370c
@ -64,6 +64,11 @@ Cached DNS hosts will be shared across the easy handles using this shared
|
||||
object. Note that when you use the multi interface, all easy handles added to
|
||||
the same multi handle will share DNS cache by default without this having to
|
||||
be used!
|
||||
.IP CURL_LOCK_DATA_SSL_SESSION
|
||||
SSL session IDs will be shared accross the easy handles using this shared
|
||||
object. This will reduce the time spent in the SSL handshake when reconnecting
|
||||
to the same server. Note SSL session IDs are reused within the same easy handle
|
||||
by default.
|
||||
.RE
|
||||
.IP CURLSHOPT_UNSHARE
|
||||
This option does the opposite of \fICURLSHOPT_SHARE\fP. It specifies that
|
||||
|
24
lib/share.c
24
lib/share.c
@ -25,6 +25,7 @@
|
||||
#include <curl/curl.h>
|
||||
#include "urldata.h"
|
||||
#include "share.h"
|
||||
#include "sslgen.h"
|
||||
#include "curl_memory.h"
|
||||
|
||||
/* The last #include file should be: */
|
||||
@ -82,7 +83,16 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
||||
break;
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
|
||||
case CURL_LOCK_DATA_SSL_SESSION: /* not supported (yet) */
|
||||
case CURL_LOCK_DATA_SSL_SESSION:
|
||||
if(!share->sslsession) {
|
||||
share->nsslsession = 8;
|
||||
share->sslsession = calloc(share->nsslsession,
|
||||
sizeof(struct curl_ssl_session));
|
||||
if(!share->sslsession)
|
||||
return CURLSHE_NOMEM;
|
||||
}
|
||||
break;
|
||||
|
||||
case CURL_LOCK_DATA_CONNECT: /* not supported (yet) */
|
||||
|
||||
default:
|
||||
@ -112,6 +122,11 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
|
||||
case CURL_LOCK_DATA_SSL_SESSION:
|
||||
if(share->sslsession) {
|
||||
free(share->sslsession);
|
||||
share->sslsession = NULL;
|
||||
share->nsslsession = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CURL_LOCK_DATA_CONNECT:
|
||||
@ -148,6 +163,7 @@ CURLSHcode
|
||||
curl_share_cleanup(CURLSH *sh)
|
||||
{
|
||||
struct Curl_share *share = (struct Curl_share *)sh;
|
||||
unsigned int i;
|
||||
|
||||
if(share == NULL)
|
||||
return CURLSHE_INVALID;
|
||||
@ -170,6 +186,12 @@ curl_share_cleanup(CURLSH *sh)
|
||||
if(share->cookies)
|
||||
Curl_cookie_cleanup(share->cookies);
|
||||
|
||||
if(share->sslsession) {
|
||||
for(i = 0; i < share->nsslsession; ++i)
|
||||
Curl_ssl_kill_session(&(share->sslsession[i]));
|
||||
free(share->sslsession);
|
||||
}
|
||||
|
||||
if(share->unlockfunc)
|
||||
share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
|
||||
free(share);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "setup.h"
|
||||
#include <curl/curl.h>
|
||||
#include "cookie.h"
|
||||
#include "urldata.h"
|
||||
|
||||
/* SalfordC says "A structure member may not be volatile". Hence:
|
||||
*/
|
||||
@ -46,6 +47,9 @@ struct Curl_share {
|
||||
|
||||
struct curl_hash *hostcache;
|
||||
struct CookieInfo *cookies;
|
||||
|
||||
struct curl_ssl_session *sslsession;
|
||||
unsigned int nsslsession;
|
||||
};
|
||||
|
||||
CURLSHcode Curl_share_lock (struct SessionHandle *, curl_lock_data,
|
||||
|
49
lib/sslgen.c
49
lib/sslgen.c
@ -62,6 +62,7 @@
|
||||
#include "url.h"
|
||||
#include "curl_memory.h"
|
||||
#include "progress.h"
|
||||
#include "share.h"
|
||||
/* The last #include file should be: */
|
||||
#include "memdebug.h"
|
||||
|
||||
@ -236,6 +237,10 @@ int Curl_ssl_getsessionid(struct connectdata *conn,
|
||||
/* session ID re-use is disabled */
|
||||
return TRUE;
|
||||
|
||||
/* Lock for reading if shared */
|
||||
if(data->share && data->share->sslsession == data->state.session)
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SHARED);
|
||||
|
||||
for(i=0; i< data->set.ssl.numsessions; i++) {
|
||||
check = &data->state.session[i];
|
||||
if(!check->sessionid)
|
||||
@ -254,13 +259,19 @@ int Curl_ssl_getsessionid(struct connectdata *conn,
|
||||
}
|
||||
}
|
||||
*ssl_sessionid = NULL;
|
||||
|
||||
/* Unlock for reading */
|
||||
if(data->share && data->share->sslsession == data->state.session)
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kill a single session ID entry in the cache.
|
||||
*/
|
||||
static int kill_session(struct curl_ssl_session *session)
|
||||
int Curl_ssl_kill_session(struct curl_ssl_session *session)
|
||||
{
|
||||
if(session->sessionid) {
|
||||
/* defensive check */
|
||||
@ -288,14 +299,23 @@ static int kill_session(struct curl_ssl_session *session)
|
||||
void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i< conn->data->set.ssl.numsessions; i++) {
|
||||
struct curl_ssl_session *check = &conn->data->state.session[i];
|
||||
struct SessionHandle *data=conn->data;
|
||||
|
||||
if(data->share && data->share->sslsession == data->state.session)
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION,
|
||||
CURL_LOCK_ACCESS_SINGLE);
|
||||
|
||||
for(i=0; i< data->set.ssl.numsessions; i++) {
|
||||
struct curl_ssl_session *check = &data->state.session[i];
|
||||
|
||||
if(check->sessionid == ssl_sessionid) {
|
||||
kill_session(check);
|
||||
Curl_ssl_kill_session(check);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(data->share && data->share->sslsession == data->state.session)
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -325,6 +345,10 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
/* Now we should add the session ID and the host name to the cache, (remove
|
||||
the oldest if necessary) */
|
||||
|
||||
/* If using shared SSL session, lock! */
|
||||
if(data->share && data->share->sslsession == data->state.session)
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
|
||||
|
||||
/* find an empty slot for us, or find the oldest */
|
||||
for(i=1; (i<data->set.ssl.numsessions) &&
|
||||
data->state.session[i].sessionid; i++) {
|
||||
@ -335,7 +359,7 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
}
|
||||
if(i == data->set.ssl.numsessions)
|
||||
/* cache is full, we must "kill" the oldest entry! */
|
||||
kill_session(store);
|
||||
Curl_ssl_kill_session(store);
|
||||
else
|
||||
store = &data->state.session[i]; /* use this slot */
|
||||
|
||||
@ -349,6 +373,11 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
store->name = clone_host; /* clone host name */
|
||||
store->remote_port = conn->remote_port; /* port number */
|
||||
|
||||
|
||||
/* Unlock */
|
||||
if(data->share && data->share->sslsession == data->state.session)
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
|
||||
if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
|
||||
store->sessionid = NULL; /* let caller free sessionid */
|
||||
free(clone_host);
|
||||
@ -363,14 +392,20 @@ void Curl_ssl_close_all(struct SessionHandle *data)
|
||||
{
|
||||
long i;
|
||||
/* kill the session ID cache */
|
||||
if(data->state.session) {
|
||||
if(data->state.session &&
|
||||
!(data->share && data->share->sslsession == data->state.session)) {
|
||||
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
|
||||
|
||||
for(i=0; i< data->set.ssl.numsessions; i++)
|
||||
/* the single-killer function handles empty table slots */
|
||||
kill_session(&data->state.session[i]);
|
||||
Curl_ssl_kill_session(&data->state.session[i]);
|
||||
|
||||
/* free the cache data */
|
||||
free(data->state.session);
|
||||
data->state.session = NULL;
|
||||
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
}
|
||||
|
||||
curlssl_close_all(data);
|
||||
|
@ -64,6 +64,8 @@ int Curl_ssl_getsessionid(struct connectdata *conn,
|
||||
CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
void *ssl_sessionid,
|
||||
size_t idsize);
|
||||
/* Kill a single session ID entry in the cache */
|
||||
int Curl_ssl_kill_session(struct curl_ssl_session *session);
|
||||
/* delete a session from the cache */
|
||||
void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid);
|
||||
|
||||
|
@ -2083,6 +2083,11 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
||||
if(data->share->cookies == data->cookies)
|
||||
data->cookies = NULL;
|
||||
|
||||
if(data->share->sslsession == data->state.session) {
|
||||
data->state.session = NULL;
|
||||
data->set.ssl.numsessions = 0;
|
||||
}
|
||||
|
||||
data->share->dirty--;
|
||||
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
|
||||
@ -2114,6 +2119,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
||||
data->cookies = data->share->cookies;
|
||||
}
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
if(data->share->sslsession) {
|
||||
data->set.ssl.numsessions = data->share->nsslsession;
|
||||
data->state.session = data->share->sslsession;
|
||||
}
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
|
||||
|
||||
}
|
||||
|
246
tests/libtest/lib586.c
Normal file
246
tests/libtest/lib586.c
Normal file
@ -0,0 +1,246 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "test.h"
|
||||
|
||||
#include <curl/mprintf.h>
|
||||
|
||||
#include "memdebug.h"
|
||||
|
||||
#define THREADS 2
|
||||
|
||||
/* struct containing data of a thread */
|
||||
struct Tdata {
|
||||
CURLSH *share;
|
||||
char *url;
|
||||
};
|
||||
|
||||
struct userdata {
|
||||
char *text;
|
||||
int counter;
|
||||
};
|
||||
|
||||
/* lock callback */
|
||||
static void my_lock(CURL *handle, curl_lock_data data, curl_lock_access laccess,
|
||||
void *useptr )
|
||||
{
|
||||
const char *what;
|
||||
struct userdata *user = (struct userdata *)useptr;
|
||||
|
||||
(void)handle;
|
||||
(void)laccess;
|
||||
|
||||
switch ( data ) {
|
||||
case CURL_LOCK_DATA_SHARE:
|
||||
what = "share";
|
||||
break;
|
||||
case CURL_LOCK_DATA_DNS:
|
||||
what = "dns";
|
||||
break;
|
||||
case CURL_LOCK_DATA_COOKIE:
|
||||
what = "cookie";
|
||||
break;
|
||||
case CURL_LOCK_DATA_SSL_SESSION:
|
||||
what = "ssl_session";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "lock: no such data: %d\n", (int)data);
|
||||
return;
|
||||
}
|
||||
printf("lock: %-6s [%s]: %d\n", what, user->text, user->counter);
|
||||
user->counter++;
|
||||
}
|
||||
|
||||
/* unlock callback */
|
||||
static void my_unlock(CURL *handle, curl_lock_data data, void *useptr )
|
||||
{
|
||||
const char *what;
|
||||
struct userdata *user = (struct userdata *)useptr;
|
||||
(void)handle;
|
||||
switch ( data ) {
|
||||
case CURL_LOCK_DATA_SHARE:
|
||||
what = "share";
|
||||
break;
|
||||
case CURL_LOCK_DATA_DNS:
|
||||
what = "dns";
|
||||
break;
|
||||
case CURL_LOCK_DATA_COOKIE:
|
||||
what = "cookie";
|
||||
break;
|
||||
case CURL_LOCK_DATA_SSL_SESSION:
|
||||
what = "ssl_session";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unlock: no such data: %d\n", (int)data);
|
||||
return;
|
||||
}
|
||||
printf("unlock: %-6s [%s]: %d\n", what, user->text, user->counter);
|
||||
user->counter++;
|
||||
}
|
||||
|
||||
/* the dummy thread function */
|
||||
static void *fire(void *ptr)
|
||||
{
|
||||
CURLcode code;
|
||||
struct Tdata *tdata = (struct Tdata*)ptr;
|
||||
CURL *curl;
|
||||
int i=0;
|
||||
|
||||
if ((curl = curl_easy_init()) == NULL) {
|
||||
fprintf(stderr, "curl_easy_init() failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, tdata->url);
|
||||
printf( "CURLOPT_SHARE\n" );
|
||||
curl_easy_setopt(curl, CURLOPT_SHARE, tdata->share);
|
||||
|
||||
printf( "PERFORM\n" );
|
||||
code = curl_easy_perform(curl);
|
||||
if( code != CURLE_OK ) {
|
||||
fprintf(stderr, "perform url '%s' repeat %d failed, curlcode %d\n",
|
||||
tdata->url, i, (int)code);
|
||||
}
|
||||
|
||||
printf( "CLEANUP\n" );
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* test function */
|
||||
int test(char *URL)
|
||||
{
|
||||
int res;
|
||||
CURLSHcode scode = CURLSHE_OK;
|
||||
char *url;
|
||||
struct Tdata tdata;
|
||||
CURL *curl;
|
||||
CURLSH *share;
|
||||
int i;
|
||||
struct userdata user;
|
||||
|
||||
user.text = (char *)"Pigs in space";
|
||||
user.counter = 0;
|
||||
|
||||
printf( "GLOBAL_INIT\n" );
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
|
||||
fprintf(stderr, "curl_global_init() failed\n");
|
||||
return TEST_ERR_MAJOR_BAD;
|
||||
}
|
||||
|
||||
/* prepare share */
|
||||
printf( "SHARE_INIT\n" );
|
||||
if ((share = curl_share_init()) == NULL) {
|
||||
fprintf(stderr, "curl_share_init() failed\n");
|
||||
curl_global_cleanup();
|
||||
return TEST_ERR_MAJOR_BAD;
|
||||
}
|
||||
|
||||
if ( CURLSHE_OK == scode ) {
|
||||
printf( "CURLSHOPT_LOCKFUNC\n" );
|
||||
scode = curl_share_setopt( share, CURLSHOPT_LOCKFUNC, my_lock);
|
||||
}
|
||||
if ( CURLSHE_OK == scode ) {
|
||||
printf( "CURLSHOPT_UNLOCKFUNC\n" );
|
||||
scode = curl_share_setopt( share, CURLSHOPT_UNLOCKFUNC, my_unlock);
|
||||
}
|
||||
if ( CURLSHE_OK == scode ) {
|
||||
printf( "CURLSHOPT_USERDATA\n" );
|
||||
scode = curl_share_setopt( share, CURLSHOPT_USERDATA, &user);
|
||||
}
|
||||
if ( CURLSHE_OK == scode ) {
|
||||
printf( "CURL_LOCK_DATA_SSL_SESSION\n" );
|
||||
scode = curl_share_setopt( share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
}
|
||||
|
||||
if ( CURLSHE_OK != scode ) {
|
||||
fprintf(stderr, "curl_share_setopt() failed\n");
|
||||
curl_share_cleanup(share);
|
||||
curl_global_cleanup();
|
||||
return TEST_ERR_MAJOR_BAD;
|
||||
}
|
||||
|
||||
|
||||
res = 0;
|
||||
|
||||
/* start treads */
|
||||
for (i=1; i<=THREADS; i++ ) {
|
||||
|
||||
/* set thread data */
|
||||
tdata.url = URL;
|
||||
tdata.share = share;
|
||||
|
||||
/* simulate thread, direct call of "thread" function */
|
||||
printf( "*** run %d\n",i );
|
||||
fire( &tdata );
|
||||
}
|
||||
|
||||
|
||||
/* fetch a another one */
|
||||
printf( "*** run %d\n", i );
|
||||
if ((curl = curl_easy_init()) == NULL) {
|
||||
fprintf(stderr, "curl_easy_init() failed\n");
|
||||
curl_share_cleanup(share);
|
||||
curl_global_cleanup();
|
||||
return TEST_ERR_MAJOR_BAD;
|
||||
}
|
||||
|
||||
url = URL;
|
||||
test_setopt( curl, CURLOPT_URL, url );
|
||||
printf( "CURLOPT_SHARE\n" );
|
||||
test_setopt( curl, CURLOPT_SHARE, share );
|
||||
|
||||
printf( "PERFORM\n" );
|
||||
curl_easy_perform( curl );
|
||||
|
||||
/* try to free share, expect to fail because share is in use*/
|
||||
printf( "try SHARE_CLEANUP...\n" );
|
||||
scode = curl_share_cleanup( share );
|
||||
if ( scode==CURLSHE_OK )
|
||||
{
|
||||
fprintf(stderr, "curl_share_cleanup succeed but error expected\n");
|
||||
share = NULL;
|
||||
} else {
|
||||
printf( "SHARE_CLEANUP failed, correct\n" );
|
||||
}
|
||||
|
||||
test_cleanup:
|
||||
|
||||
/* clean up last handle */
|
||||
printf( "CLEANUP\n" );
|
||||
curl_easy_cleanup( curl );
|
||||
|
||||
/* free share */
|
||||
printf( "SHARE_CLEANUP\n" );
|
||||
scode = curl_share_cleanup( share );
|
||||
if ( scode!=CURLSHE_OK )
|
||||
fprintf(stderr, "curl_share_cleanup failed, code errno %d\n",
|
||||
(int)scode);
|
||||
|
||||
printf( "GLOBAL_CLEANUP\n" );
|
||||
curl_global_cleanup();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user