diff --git a/src/ssl.c b/src/ssl.c index fb191d53..4853459d 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -8,18 +8,39 @@ #include "ssl.h" -SSL_CTX *ssl_init() { +int ssl_data_index; + +static int ssl_new_client_session(SSL *ssl, SSL_SESSION *session) { + connection *c = SSL_get_ex_data(ssl, ssl_data_index); + + if (c->cache) { + if (c->cache->cached_session) { + SSL_SESSION_free(c->cache->cached_session); + c->cache->cached_session = NULL; + } + c->cache->cached_session = session; + } + + return 1; +} + +SSL_CTX *ssl_init(bool tls_session_reuse) { SSL_CTX *ctx = NULL; SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); + ssl_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); if ((ctx = SSL_CTX_new(SSLv23_client_method()))) { SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); SSL_CTX_set_verify_depth(ctx, 0); SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); + + if (tls_session_reuse) { + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx, ssl_new_client_session); + } } return ctx; @@ -27,6 +48,11 @@ SSL_CTX *ssl_init() { status ssl_connect(connection *c, char *host) { int r; + + if (SSL_get_fd(c->ssl) != c->fd && c->cache && c->cache->cached_session) { + SSL_set_session(c->ssl, c->cache->cached_session); + } + SSL_set_fd(c->ssl, c->fd); SSL_set_tlsext_host_name(c->ssl, host); if ((r = SSL_connect(c->ssl)) != 1) { @@ -42,6 +68,8 @@ status ssl_connect(connection *c, char *host) { status ssl_close(connection *c) { SSL_shutdown(c->ssl); SSL_clear(c->ssl); + SSL_free(c->ssl); + c->ssl = NULL; return OK; } diff --git a/src/ssl.h b/src/ssl.h index 181d5b31..6a605aaf 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -3,7 +3,7 @@ #include "net.h" -SSL_CTX *ssl_init(); +SSL_CTX *ssl_init(bool); status ssl_connect(connection *, char *); status ssl_close(connection *); @@ -11,4 +11,6 @@ status ssl_read(connection *, size_t *); status ssl_write(connection *, char *, size_t, size_t *); size_t ssl_readable(connection *); +extern int ssl_data_index; + #endif /* SSL_H */ diff --git a/src/wrk.c b/src/wrk.c index 51f46f72..fcf63b12 100644 --- a/src/wrk.c +++ b/src/wrk.c @@ -13,6 +13,7 @@ static struct config { bool delay; bool dynamic; bool latency; + bool tls_session_reuse; char *host; char *script; SSL_CTX *ctx; @@ -52,6 +53,7 @@ static void usage() { " -H, --header Add header to request \n" " --latency Print latency statistics \n" " --timeout Socket/request timeout \n" + " -r, --reuse Enable TLS session reuse \n" " -v, --version Print version details \n" " \n" " Numeric arguments may include a SI unit (1k, 1M, 1G)\n" @@ -73,7 +75,7 @@ int main(int argc, char **argv) { char *service = port ? port : schema; if (!strncmp("https", schema, 5)) { - if ((cfg.ctx = ssl_init()) == NULL) { + if ((cfg.ctx = ssl_init(cfg.tls_session_reuse)) == NULL) { fprintf(stderr, "unable to initialize SSL\n"); ERR_print_errors_fp(stderr); exit(1); @@ -136,7 +138,13 @@ int main(int argc, char **argv) { char *time = format_time_s(cfg.duration); printf("Running %s test @ %s\n", time, url); - printf(" %"PRIu64" threads and %"PRIu64" connections\n", cfg.threads, cfg.connections); + printf(" %"PRIu64" threads and %"PRIu64" connections", cfg.threads, cfg.connections); + + if (cfg.ctx) { + printf(" (TLS session reuse %s)", cfg.tls_session_reuse ? "enabled" : "disabled"); + } + + printf("\n"); uint64_t start = time_us(); uint64_t complete = 0; @@ -190,6 +198,19 @@ int main(int argc, char **argv) { printf("Requests/sec: %9.2Lf\n", req_per_s); printf("Transfer/sec: %10sB\n", format_binary(bytes_per_s)); + if (cfg.ctx) { + printf("TLS new conn %lu reused %lu miss %lu finished conn %lu sess_cb_hit %lu renegotiation %lu timeout %lu full remove %lu\n", + SSL_CTX_sess_connect(cfg.ctx), + SSL_CTX_sess_hits(cfg.ctx), + SSL_CTX_sess_misses(cfg.ctx), + SSL_CTX_sess_connect_good(cfg.ctx), + SSL_CTX_sess_cb_hits(cfg.ctx), + SSL_CTX_sess_connect_renegotiate(cfg.ctx), + SSL_CTX_sess_timeouts(cfg.ctx), + SSL_CTX_sess_cache_full(cfg.ctx) + ); + } + if (script_has_done(L)) { script_summary(L, runtime_us, complete, bytes); script_errors(L, &errors); @@ -214,7 +235,6 @@ void *thread_main(void *arg) { for (uint64_t i = 0; i < thread->connections; i++, c++) { c->thread = thread; - c->ssl = cfg.ctx ? SSL_new(cfg.ctx) : NULL; c->request = request; c->length = length; c->delayed = cfg.delay; @@ -227,6 +247,7 @@ void *thread_main(void *arg) { thread->start = time_us(); aeMain(loop); + SSL_SESSION_free(thread->cache.cached_session); aeDeleteEventLoop(loop); zfree(thread->cs); @@ -250,6 +271,12 @@ static int connect_socket(thread *thread, connection *c) { flags = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags)); + if (cfg.ctx) { + c->ssl = cfg.ctx ? SSL_new(cfg.ctx) : NULL; + SSL_set_ex_data(c->ssl, ssl_data_index, c); + c->cache = cfg.tls_session_reuse ? &thread->cache : NULL; + } + flags = AE_READABLE | AE_WRITABLE; if (aeCreateFileEvent(loop, fd, flags, socket_connected, c) == AE_OK) { c->parser.data = c; @@ -475,6 +502,7 @@ static struct option longopts[] = { { "latency", no_argument, NULL, 'L' }, { "timeout", required_argument, NULL, 'T' }, { "help", no_argument, NULL, 'h' }, + { "reuse", no_argument, NULL, 'r' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; @@ -517,6 +545,9 @@ static int parse_args(struct config *cfg, char **url, struct http_parser_url *pa printf("wrk %s [%s] ", VERSION, aeGetApiName()); printf("Copyright (C) 2012 Will Glozer\n"); break; + case 'r': + cfg->tls_session_reuse = true; + break; case 'h': case '?': case ':': diff --git a/src/wrk.h b/src/wrk.h index 2d0ac84e..0f9fdea3 100644 --- a/src/wrk.h +++ b/src/wrk.h @@ -24,6 +24,10 @@ extern const char *VERSION; +typedef struct { + SSL_SESSION *cached_session; +} tls_session_cache; + typedef struct { pthread_t thread; aeEventLoop *loop; @@ -35,6 +39,7 @@ typedef struct { uint64_t start; lua_State *L; errors errors; + tls_session_cache cache; struct connection *cs; } thread; @@ -52,6 +57,7 @@ typedef struct connection { } state; int fd; SSL *ssl; + tls_session_cache *cache; bool delayed; uint64_t start; char *request;