You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@trafficserver.apache.org by James Peach <jp...@apache.org> on 2014/10/09 18:02:00 UTC

Re: [1/3] git commit: TS-3080: Optimized SSL Session Cache

I thought you were going to format to our style guidelines before committing?

On Oct 8, 2014, at 11:34 AM, briang@apache.org wrote:

> Repository: trafficserver
> Updated Branches:
>  refs/heads/master 195259b16 -> f1bedb41e
> 
> 
> TS-3080: Optimized SSL Session Cache
> 
> 
> Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
> Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/53bf5d1e
> Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/53bf5d1e
> Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/53bf5d1e
> 
> Branch: refs/heads/master
> Commit: 53bf5d1e7618ae38b0a8b49263a047282eec68d1
> Parents: 72b7c05
> Author: Brian Geffon <br...@apache.org>
> Authored: Tue Oct 7 18:51:34 2014 -0700
> Committer: Brian Geffon <br...@apache.org>
> Committed: Tue Oct 7 18:52:34 2014 -0700
> 
> ----------------------------------------------------------------------
> iocore/net/Makefile.am        |   1 +
> iocore/net/P_SSLConfig.h      |  12 +-
> iocore/net/P_SSLUtils.h       |   4 +
> iocore/net/SSLConfig.cc       |  19 ++-
> iocore/net/SSLSessionCache.cc | 246 +++++++++++++++++++++++++++++++++++++
> iocore/net/SSLSessionCache.h  | 149 ++++++++++++++++++++++
> iocore/net/SSLUtils.cc        |  98 ++++++++++++++-
> lib/ts/ink_mutex.h            |  29 +++++
> mgmt/RecordsConfig.cc         |   8 +-
> proxy/Makefile.am             |   2 +-
> 10 files changed, 559 insertions(+), 9 deletions(-)
> ----------------------------------------------------------------------
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/Makefile.am
> ----------------------------------------------------------------------
> diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
> index 0120528..da7a476 100644
> --- a/iocore/net/Makefile.am
> +++ b/iocore/net/Makefile.am
> @@ -88,6 +88,7 @@ libinknet_a_SOURCES = \
>   P_UnixUDPConnection.h \
>   Socks.cc \
>   SSLCertLookup.cc \
> +  SSLSessionCache.cc \
>   SSLConfig.cc \
>   SSLNetAccept.cc \
>   SSLNetProcessor.cc \
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/P_SSLConfig.h
> ----------------------------------------------------------------------
> diff --git a/iocore/net/P_SSLConfig.h b/iocore/net/P_SSLConfig.h
> index aa4926f..0cad7d9 100644
> --- a/iocore/net/P_SSLConfig.h
> +++ b/iocore/net/P_SSLConfig.h
> @@ -32,6 +32,7 @@
> #define __P_SSLCONFIG_H__
> 
> #include "ProxyConfig.h"
> +#include "SSLSessionCache.h"
> 
> struct SSLCertLookup;
> 
> @@ -51,7 +52,8 @@ struct SSLConfigParams : public ConfigInfo
>   enum SSL_SESSION_CACHE_MODE
>   {
>     SSL_SESSION_CACHE_MODE_OFF = 0,
> -    SSL_SESSION_CACHE_MODE_SERVER = 1
> +    SSL_SESSION_CACHE_MODE_SERVER_OPENSSL_IMPL = 1,
> +    SSL_SESSION_CACHE_MODE_SERVER_ATS_IMPL = 2
>   };
> 
>   SSLConfigParams();
> @@ -69,6 +71,8 @@ struct SSLConfigParams : public ConfigInfo
>   int     verify_depth;
>   int     ssl_session_cache; // SSL_SESSION_CACHE_MODE
>   int     ssl_session_cache_size;
> +  int     ssl_session_cache_num_buckets;
> +  int     ssl_session_cache_skip_on_contention;
>   int     ssl_session_cache_timeout;
> 
>   char *  clientCertPath;
> @@ -88,6 +92,10 @@ struct SSLConfigParams : public ConfigInfo
>   static int  ssl_ocsp_request_timeout;
>   static int  ssl_ocsp_update_period;
> 
> +  static size_t session_cache_number_buckets;
> +  static size_t session_cache_max_bucket_size;
> +  static bool session_cache_skip_on_lock_contention;
> +
>   static init_ssl_ctx_func init_ssl_ctx_cb;
> 
>   void initialize();
> @@ -126,4 +134,6 @@ private:
>   static int configid;
> };
> 
> +extern SSLSessionCache *session_cache;
> +
> #endif
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/P_SSLUtils.h
> ----------------------------------------------------------------------
> diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
> index 3cf0c20..1c9f0b8 100644
> --- a/iocore/net/P_SSLUtils.h
> +++ b/iocore/net/P_SSLUtils.h
> @@ -70,6 +70,10 @@ enum SSL_Stats
>   ssl_total_tickets_verified_stat,
>   ssl_total_tickets_not_found_stat,
>   ssl_total_tickets_renewed_stat,
> +  ssl_session_cache_hit,
> +  ssl_session_cache_miss,
> +  ssl_session_cache_eviction,
> +  ssl_session_cache_lock_contention,
> 
>   /* error stats */
>   ssl_error_want_write,
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/SSLConfig.cc
> ----------------------------------------------------------------------
> diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
> index 402664a..3aaddc1 100644
> --- a/iocore/net/SSLConfig.cc
> +++ b/iocore/net/SSLConfig.cc
> @@ -37,6 +37,7 @@
> #include "P_SSLConfig.h"
> #include "P_SSLUtils.h"
> #include "P_SSLCertLookup.h"
> +#include "SSLSessionCache.h"
> #include <records/I_RecHttp.h>
> 
> int SSLConfig::configid = 0;
> @@ -47,6 +48,10 @@ bool SSLConfigParams::ssl_ocsp_enabled = false;
> int SSLConfigParams::ssl_ocsp_cache_timeout = 3600;
> int SSLConfigParams::ssl_ocsp_request_timeout = 10;
> int SSLConfigParams::ssl_ocsp_update_period = 60;
> +size_t SSLConfigParams::session_cache_number_buckets = 1024;
> +bool SSLConfigParams::session_cache_skip_on_lock_contention = false;
> +size_t SSLConfigParams::session_cache_max_bucket_size = 100;
> +
> init_ssl_ctx_func SSLConfigParams::init_ssl_ctx_cb = NULL;
> 
> static ConfigUpdateHandler<SSLCertificateConfig> * sslCertUpdate;
> @@ -70,8 +75,10 @@ SSLConfigParams::SSLConfigParams()
> 
>   ssl_ctx_options = 0;
>   ssl_client_ctx_protocols = 0;
> -  ssl_session_cache = SSL_SESSION_CACHE_MODE_SERVER;
> -  ssl_session_cache_size = 1024*20;
> +  ssl_session_cache = SSL_SESSION_CACHE_MODE_SERVER_ATS_IMPL;
> +  ssl_session_cache_size = 1024*100;
> +  ssl_session_cache_num_buckets = 1024; // Sessions per bucket is ceil(ssl_session_cache_size / ssl_session_cache_num_buckets)
> +  ssl_session_cache_skip_on_contention = 0;
>   ssl_session_cache_timeout = 0;
> }
> 
> @@ -248,8 +255,16 @@ SSLConfigParams::initialize()
>   // SSL session cache configurations
>   REC_ReadConfigInteger(ssl_session_cache, "proxy.config.ssl.session_cache");
>   REC_ReadConfigInteger(ssl_session_cache_size, "proxy.config.ssl.session_cache.size");
> +  REC_ReadConfigInteger(ssl_session_cache_num_buckets, "proxy.config.ssl.session_cache.num_buckets");
> +  REC_ReadConfigInteger(ssl_session_cache_skip_on_contention, "proxy.config.ssl.session_cache.skip_cache_on_bucket_contention");
>   REC_ReadConfigInteger(ssl_session_cache_timeout, "proxy.config.ssl.session_cache.timeout");
> 
> +  SSLConfigParams::session_cache_max_bucket_size = ceil(ssl_session_cache_size/ssl_session_cache_num_buckets );
> +  SSLConfigParams::session_cache_skip_on_lock_contention = ssl_session_cache_skip_on_contention;
> +  SSLConfigParams::session_cache_number_buckets = ssl_session_cache_num_buckets;
> +
> +  session_cache = new SSLSessionCache();
> +
>   // SSL record size
>   REC_EstablishStaticConfigInt32(ssl_maxrecord, "proxy.config.ssl.max_record_size");
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/SSLSessionCache.cc
> ----------------------------------------------------------------------
> diff --git a/iocore/net/SSLSessionCache.cc b/iocore/net/SSLSessionCache.cc
> new file mode 100644
> index 0000000..c936ee7
> --- /dev/null
> +++ b/iocore/net/SSLSessionCache.cc
> @@ -0,0 +1,246 @@
> +/** @file
> +
> +  @section license License
> +
> +  Licensed to the Apache Software Foundation (ASF) under one
> +  or more contributor license agreements.  See the NOTICE file
> +  distributed with this work for additional information
> +  regarding copyright ownership.  The ASF licenses this file
> +  to you under the Apache License, Version 2.0 (the
> +  "License"); you may not use this file except in compliance
> +  with the License.  You may obtain a copy of the License at
> +
> +      http://www.apache.org/licenses/LICENSE-2.0
> +
> +  Unless required by applicable law or agreed to in writing, software
> +  distributed under the License is distributed on an "AS IS" BASIS,
> +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +  See the License for the specific language governing permissions and
> +  limitations under the License.
> + */
> +
> +#include <cstring>
> +#include <deque>
> +#include "P_SSLConfig.h"
> +#include "SSLSessionCache.h"
> +
> +#define SSLSESSIONCACHE_STRINGIFY0(x) #x
> +#define SSLSESSIONCACHE_STRINGIFY(x) SSLSESSIONCACHE_STRINGIFY0(x)
> +#define SSLSESSIONCACHE_LINENO SSLSESSIONCACHE_STRINGIFY(__LINE__)
> +
> +#ifdef DEBUG
> +#define PRINT_BUCKET(x) this->print(x " at " __FILE__ ":" SSLSESSIONCACHE_LINENO);
> +#else
> +#define PRINT_BUCKET(x)
> +#endif
> +
> +using ts::detail::RBNode;
> +
> +/* Session Cache */
> +SSLSessionCache::SSLSessionCache()
> +  : session_bucket(NULL) {
> +  Debug("ssl.session_cache", "Created new ssl session cache %p with %ld buckets each with size max size %ld", this, SSLConfigParams::session_cache_number_buckets, SSLConfigParams::session_cache_max_bucket_size);
> +
> +  session_bucket = new SSLSessionBucket[SSLConfigParams::session_cache_number_buckets];
> +}
> +
> +SSLSessionCache::~SSLSessionCache() {
> +  delete []session_bucket;
> +}
> +
> +bool SSLSessionCache::getSession(const SSLSessionID &sid, const char *sni_name, SSL_SESSION **sess) {
> +  uint64_t hash = sid.hash();
> +  uint64_t target_bucket = hash % SSLConfigParams::session_cache_number_buckets;
> +  SSLSessionBucket *bucket = &session_bucket[target_bucket];
> +  bool ret = false;
> +
> +  if (is_debug_tag_set("ssl.session_cache")) {
> +     char buf[sid.len * 2 + 1];
> +     sid.toString(buf, sizeof(buf));
> +     Debug("ssl.session_cache.get", "SessionCache looking in bucket %" PRId64 " (%p) for session '%s' (hash: %" PRIX64 ").", target_bucket, bucket, buf, hash);
> +   }
> +
> +  ret = bucket->getSession(sid, sni_name, sess);
> +
> +  if (ret)
> +    SSL_INCREMENT_DYN_STAT(ssl_session_cache_hit);
> +  else
> +    SSL_INCREMENT_DYN_STAT(ssl_session_cache_miss);
> +
> +  return ret;
> +}
> +
> +void SSLSessionCache::removeSession(const SSLSessionID &sid) {
> +  uint64_t hash = sid.hash();
> +  uint64_t target_bucket = hash % SSLConfigParams::session_cache_number_buckets;
> +  SSLSessionBucket *bucket = &session_bucket[target_bucket];
> +
> +  if (is_debug_tag_set("ssl.session_cache")) {
> +     char buf[sid.len * 2 + 1];
> +     sid.toString(buf, sizeof(buf));
> +     Debug("ssl.session_cache.remove", "SessionCache using bucket %" PRId64 " (%p): Removing session '%s' (hash: %" PRIX64 ").", target_bucket, bucket, buf, hash);
> +   }
> +
> +  SSL_INCREMENT_DYN_STAT(ssl_session_cache_eviction);
> +  bucket->removeSession(sid);
> +}
> +
> +void SSLSessionCache::insertSession(const SSLSessionID &sid, const char *sni_name, SSL_SESSION *sess) {
> +  uint64_t hash = sid.hash();
> +  uint64_t target_bucket = hash % SSLConfigParams::session_cache_number_buckets;
> +  SSLSessionBucket *bucket = &session_bucket[target_bucket];
> +
> +  if (is_debug_tag_set("ssl.session_cache")) {
> +     char buf[sid.len * 2 + 1];
> +     sid.toString(buf, sizeof(buf));
> +     Debug("ssl.session_cache.insert", "SessionCache using bucket %" PRId64 " (%p): Inserting session '%s' (hash: %" PRIX64 ").", target_bucket, bucket, buf, hash);
> +   }
> +
> +  bucket->insertSession(sid, sni_name, sess);
> +}
> +
> +void SSLSessionBucket::insertSession(const SSLSessionID &id, const char *sni_name, SSL_SESSION *sess) {
> +  size_t len = i2d_SSL_SESSION(sess, NULL); // make sure we're not going to need more than SSL_MAX_SESSION_SIZE bytes
> +  /* do not cache a session that's too big. */
> +  if (len > (size_t) SSL_MAX_SESSION_SIZE) {
> +      Debug("ssl.session_cache", "Unable to save SSL session because size of %" PRId64 " exceeds the max of %d", len, SSL_MAX_SESSION_SIZE);
> +      return;
> +  }
> +
> +  if (is_debug_tag_set("ssl.session_cache")) {
> +    char buf[id.len * 2 + 1];
> +    id.toString(buf, sizeof(buf));
> +    Debug("ssl.session_cache", "Inserting session '%s' to bucket %p with sni name '%s'", buf, this, sni_name);
> +  }
> +
> +  Ptr<IOBufferData> buf;
> +  buf = new_IOBufferData(buffer_size_to_index(len, MAX_BUFFER_SIZE_INDEX), MEMALIGNED);
> +  ink_release_assert(static_cast<size_t>(buf->block_size()) >= len);
> +  unsigned char *loc = reinterpret_cast<unsigned char *>(buf->data());
> +  i2d_SSL_SESSION(sess, &loc);
> +
> +  SSLSession *ssl_session = new SSLSession(id, sni_name, buf, len);
> +
> +  ink_scoped_try_mutex scoped_mutex(mutex);
> +  if (!scoped_mutex.hasLock()) {
> +    SSL_INCREMENT_DYN_STAT(ssl_session_cache_lock_contention);
> +    if (SSLConfigParams::session_cache_skip_on_lock_contention)
> +      return;
> +
> +    scoped_mutex.lock();
> +  }
> +
> +  PRINT_BUCKET("insertSession before")
> +  if (queue.size >= static_cast<int>(SSLConfigParams::session_cache_max_bucket_size)) {
> +      removeOldestSession();
> +  }
> +
> +  /* do the actual insert */
> +  queue.enqueue(ssl_session);
> +
> +  PRINT_BUCKET("insertSession after")
> +}
> +
> +
> +
> +bool SSLSessionBucket::getSession(const SSLSessionID &id, const char *sni_name, SSL_SESSION **sess) {
> +  char buf[id.len * 2 + 1];
> +  if (is_debug_tag_set("ssl.session_cache")) {
> +   id.toString(buf, sizeof(buf));
> +  }
> +
> +  Debug("ssl.session_cache", "Looking for session with id '%s' in bucket %p with sni name '%s'", buf, this, sni_name);
> +
> +  ink_scoped_try_mutex scoped_mutex(mutex);
> +  if (!scoped_mutex.hasLock()) {
> +   SSL_INCREMENT_DYN_STAT(ssl_session_cache_lock_contention);
> +   if (SSLConfigParams::session_cache_skip_on_lock_contention)
> +     return false;
> +   scoped_mutex.lock();
> +  }
> +
> +  PRINT_BUCKET("getSession")
> +
> +  // We work backwards because that's the most likely place we'll find our session...
> +  SSLSession *node = queue.tail;
> +  while (node) {
> +    if (node->session_id == id)
> +    {
> +      if ((node->sni_name == NULL && sni_name == NULL) /* this session doesn't have an associated SNI name */||
> +         (node->sni_name && sni_name && strcmp(node->sni_name, sni_name) == 0)) { /* the session does have an associated SNI name */
> +       Debug("ssl.session_cache", "Found session with id '%s' in bucket %p with sni name '%s'.", buf, this, sni_name);
> +
> +       const unsigned char *loc = reinterpret_cast<const unsigned char *>(node->asn1_data->data());
> +       *sess = d2i_SSL_SESSION(NULL, &loc, node->len_asn1_data);
> +
> +       return true;
> +      } else {
> +       Debug("ssl.session_cache", "Found session with id '%s' in bucket %p but sni names didn't match! '%s' != '%s'.", buf, this, node->sni_name, sni_name);
> +       return false;
> +      }
> +    }
> +
> +    node = node->link.prev;
> +  }
> +
> +  Debug("ssl.session_cache", "Session with id '%s' not found in bucket %p.", buf, this);
> +  return false;
> +}
> +
> +void inline SSLSessionBucket::print(const char *ref_str) const {
> +  /* NOTE: This method assumes you're already holding the bucket lock */
> +  if (!is_debug_tag_set("ssl.session_cache.bucket")) {
> +     return;
> +  }
> +
> +  fprintf(stderr, "-------------- BUCKET %p (%s) ----------------\n", this, ref_str);
> +  fprintf(stderr, "Current Size: %d, Max Size: %" PRId64 "\n", queue.size, SSLConfigParams::session_cache_max_bucket_size);
> +  fprintf(stderr, "Queue: \n");
> +
> +  SSLSession *node = queue.head;
> +  while(node) {
> +    char s_buf[2 * node->session_id.len + 1];
> +    node->session_id.toString(s_buf, sizeof(s_buf));
> +    fprintf(stderr, "  %s\n", s_buf);
> +    node = node->link.next;
> +  }
> +}
> +
> +void inline SSLSessionBucket::removeOldestSession() {
> +  PRINT_BUCKET("removeOldestSession before")
> +  while (queue.head && queue.size >= static_cast<int>(SSLConfigParams::session_cache_max_bucket_size)) {
> +    SSLSession *old_head = queue.pop();
> +    if (is_debug_tag_set("ssl.session_cache")) {
> +      char buf[old_head->session_id.len * 2 + 1];
> +      old_head->session_id.toString(buf, sizeof(buf));
> +      Debug("ssl.session_cache", "Removing session '%s' from bucket %p because the bucket has size %d and max %" PRId64, buf, this, (queue.size + 1), SSLConfigParams::session_cache_max_bucket_size);
> +    }
> +    delete old_head;
> +  }
> +  PRINT_BUCKET("removeOldestSession after")
> +}
> +
> +void SSLSessionBucket::removeSession(const SSLSessionID &id) {
> +  ink_scoped_mutex scoped_mutex(mutex); // We can't bail on contention here because this session MUST be removed.
> +  SSLSession *node = queue.head;
> +  while (node) {
> +    if (node->session_id == id)
> +    {
> +      queue.remove(node);
> +      delete node;
> +      return;
> +    }
> +  }
> +}
> +
> +/* Session Bucket */
> +SSLSessionBucket::SSLSessionBucket() : root(NULL) {
> +  Debug("ssl.session_cache", "Created new bucket %p with max size %ld", this, SSLConfigParams::session_cache_max_bucket_size);
> +	ink_mutex_init(&mutex, "session_bucket");
> +}
> +
> +SSLSessionBucket::~SSLSessionBucket() {
> +	ink_mutex_destroy(&mutex);
> +}
> +
> +
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/SSLSessionCache.h
> ----------------------------------------------------------------------
> diff --git a/iocore/net/SSLSessionCache.h b/iocore/net/SSLSessionCache.h
> new file mode 100644
> index 0000000..283438a
> --- /dev/null
> +++ b/iocore/net/SSLSessionCache.h
> @@ -0,0 +1,149 @@
> +/** @file
> +
> +  @section license License
> +
> +  Licensed to the Apache Software Foundation (ASF) under one
> +  or more contributor license agreements.  See the NOTICE file
> +  distributed with this work for additional information
> +  regarding copyright ownership.  The ASF licenses this file
> +  to you under the Apache License, Version 2.0 (the
> +  "License"); you may not use this file except in compliance
> +  with the License.  You may obtain a copy of the License at
> +
> +      http://www.apache.org/licenses/LICENSE-2.0
> +
> +  Unless required by applicable law or agreed to in writing, software
> +  distributed under the License is distributed on an "AS IS" BASIS,
> +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +  See the License for the specific language governing permissions and
> +  limitations under the License.
> + */
> +
> +#ifndef SSL_SESSION_CACHE_
> +#define SSL_SESSION_CACHE_
> +#include "Map.h"
> +#include "List.h"
> +#include "ink_mutex.h"
> +#include "P_EventSystem.h"
> +#include "P_AIO.h"
> +#include "I_RecProcess.h"
> +#include "libts.h"
> +#include "P_SSLUtils.h"
> +#include "RbTree.h"
> +#include <openssl/ssl.h>
> +
> +#define SSL_MAX_SESSION_SIZE 2048
> +
> +struct SSLSessionID {
> +  char bytes[SSL_MAX_SSL_SESSION_ID_LENGTH];
> +  size_t len;
> +
> +  SSLSessionID(const unsigned char *s, size_t l) : len(l) {
> +    ink_release_assert(l <= sizeof(bytes));
> +    memcpy(bytes, s, l);
> +  }
> +
> +  SSLSessionID(const SSLSessionID& other) {
> +    if (other.len)
> +      memcpy(bytes, other.bytes, other.len);
> +
> +    len = other.len;
> +  }
> +
> + bool operator<(const SSLSessionID &other) const {
> +   if (len != other.len)
> +     return len < other.len;
> +
> +   return (memcmp(bytes, other.bytes, len) < 0);
> + }
> +
> +  SSLSessionID& operator=(const SSLSessionID& other) {
> +    if (other.len)
> +      memcpy(bytes, other.bytes, other.len);
> +
> +    len = other.len;
> +    return *this;
> +  }
> +
> +  bool operator==(const SSLSessionID &other) const {
> +   if (len != other.len)
> +       return false;
> +
> +   // memcmp returns 0 on equal
> +   return (memcmp(bytes, other.bytes, len) == 0);
> +  }
> +
> +  const char *toString(char *buf, size_t buflen) const {
> +    char *cur_pos = buf;
> +    for (size_t i = 0; i < len && buflen > 0; ++i) {
> +      if (buflen > 2) { // we have enough space for 3 bytes, 2 hex and 1 null terminator
> +        snprintf(cur_pos, 3 /* including a null terminator */, "%02hhX", static_cast<unsigned char>(bytes[i]));
> +        cur_pos += 2;
> +        buflen -= 2;
> +      } else { // not enough space for any more hex bytes, just null terminate
> +        *cur_pos = '\0';
> +        break;
> +      }
> +    }
> +    return buf;
> +  }
> +
> +  uint64_t hash() const {
> +    // because the session ids should be uniformly random let's just use the upper 64 bits as the hash.
> +    if (len >= sizeof(uint64_t))
> +      return *reinterpret_cast<uint64_t *>(const_cast<char *>(bytes));
> +    else if (len)
> +      return static_cast<uint64_t>(bytes[0]);
> +    else
> +      return 0;
> +  }
> +
> +};
> +
> +class SSLSession {
> +public:
> +  SSLSessionID session_id;
> +  const char *sni_name;
> +  Ptr<IOBufferData> asn1_data; /* this is the ASN1 representation of the SSL_CTX */
> +  size_t len_asn1_data;
> +
> +  SSLSession(const SSLSessionID &id, const char *name, Ptr<IOBufferData> ssl_asn1_data, size_t len_asn1)
> + : session_id(id), sni_name(name), asn1_data(ssl_asn1_data), len_asn1_data(len_asn1)
> +  { }
> +
> +	LINK(SSLSession, link);
> +};
> +
> +class SSLSessionBucket {
> +public:
> +  SSLSessionBucket();
> +  ~SSLSessionBucket();
> +  void removeOldestSession();
> +  void insertSession(const SSLSessionID &, const char *sni_name, SSL_SESSION *ctx);
> +  bool getSession(const SSLSessionID &, const char *sni_name, SSL_SESSION **ctx);
> +  void removeSession(const SSLSessionID &);
> +
> +private:
> +  /* these method must be used while hold the lock */
> +  void print(const char *) const;
> +
> +  mutable ink_mutex mutex;
> +  CountQueue<SSLSession> queue;
> +  SSLSession *root;
> +};
> +
> +class SSLSessionCache {
> +public:
> +	bool getSession(const SSLSessionID &sid, const char *sni_name, SSL_SESSION **sess);
> +	void insertSession(const SSLSessionID &sid, const char *sni_name, SSL_SESSION *sess);
> +	void removeSession(const SSLSessionID &sid);
> +  SSLSessionCache();
> +  ~SSLSessionCache();
> +
> + private:
> +    SSLSessionBucket *session_bucket;
> +};
> +
> +#endif
> +
> +
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/iocore/net/SSLUtils.cc
> ----------------------------------------------------------------------
> diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
> index 3bf90d7..7fede11 100644
> --- a/iocore/net/SSLUtils.cc
> +++ b/iocore/net/SSLUtils.cc
> @@ -26,6 +26,7 @@
> #include "P_Net.h"
> #include "ink_cap.h"
> #include "P_OCSPStapling.h"
> +#include "SSLSessionCache.h"
> 
> #include <string>
> #include <openssl/err.h>
> @@ -106,6 +107,7 @@ struct ssl_user_config
> 
> #define HAVE_OPENSSL_SESSION_TICKETS 1
> 
> +SSLSessionCache *session_cache; // declared extern in P_SSLConfig.h
> static void session_ticket_free(void *, void *, CRYPTO_EX_DATA *, int, long, void *);
> static int ssl_callback_session_ticket(SSL *, unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int);
> #endif /* SSL_CTX_set_tlsext_ticket_key_cb */
> @@ -176,6 +178,61 @@ SSL_CTX_add_extra_chain_cert_file(SSL_CTX * ctx, const char * chainfile)
>   return true;
> }
> 
> +
> +static SSL_SESSION* ssl_get_cached_session(SSL *ssl, unsigned char *id, int len, int *copy) {
> +  const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
> +  *copy = 0;
> +
> +  SSLSessionID sid(id, len);
> +  if (diags->tag_activated("ssl.session_cache")) {
> +    char printable_buf[(len * 2) + 1];
> +    sid.toString(printable_buf, sizeof(printable_buf));
> +    Debug("ssl.session_cache.get", "ssl_get_cached_session cached session '%s' on name '%s'", printable_buf, servername);
> +  }
> +
> +  SSL_SESSION *session = NULL;
> +  if(session_cache->getSession(sid, servername, &session))
> +    return session;
> +  else
> +    return NULL;
> +
> +}
> +
> +static int ssl_new_cached_session(SSL *ssl, SSL_SESSION *sess) {
> +  unsigned int len = 0;
> +  const unsigned char *id = SSL_SESSION_get_id(sess, &len);
> +  const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
> +
> +  SSLSessionID sid(id, len);
> +  if (diags->tag_activated("ssl.session_cache")) {
> +    char printable_buf[(len * 2) + 1];
> +    sid.toString(printable_buf, sizeof(printable_buf));
> +    Debug("ssl.session_cache.insert", "ssl_new_cached_session session '%s' on name '%s'", printable_buf, servername);
> +  }
> +
> +  session_cache->insertSession(sid, servername, sess);
> +
> +  return 0;
> +}
> +
> +static void ssl_rm_cached_session(SSL_CTX *ctx, SSL_SESSION *sess) {
> +  SSL_CTX_remove_session(ctx, sess);
> +
> +  unsigned int len = 0;
> +  const unsigned char *id = SSL_SESSION_get_id(sess, &len);
> +
> +  SSLSessionID sid(id, len);
> +  if (diags->tag_activated("ssl.session_cache")) {
> +    char printable_buf[(len * 2) + 1];
> +    sid.toString(printable_buf, sizeof(printable_buf));
> +    Debug("ssl.session_cache.remove", "ssl_rm_cached_session cached session '%s'", printable_buf);
> +  }
> +
> +  session_cache->removeSession(sid);
> +}
> +
> +
> +
> #if TS_USE_TLS_SNI
> 
> static int
> @@ -691,6 +748,22 @@ SSLInitializeStatistics()
>                      RECD_INT, RECP_PERSISTENT, (int) ssl_total_tickets_renewed_stat,
>                      RecRawStatSyncCount);
> 
> +  RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.ssl_session_cache_hit",
> +                     RECD_INT, RECP_PERSISTENT, (int) ssl_session_cache_hit,
> +                     RecRawStatSyncCount);
> +
> +  RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.ssl_session_cache_miss",
> +                     RECD_INT, RECP_PERSISTENT, (int) ssl_session_cache_miss,
> +                     RecRawStatSyncCount);
> +
> +  RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.ssl_session_cache_eviction",
> +                     RECD_INT, RECP_PERSISTENT, (int) ssl_session_cache_eviction,
> +                     RecRawStatSyncCount);
> +
> +  RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.ssl_session_cache_lock_contention",
> +                     RECD_INT, RECP_PERSISTENT, (int) ssl_session_cache_lock_contention,
> +                     RecRawStatSyncCount);
> +
>   /* error stats */
>   RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.ssl_error_want_write",
>                      RECD_INT, RECP_PERSISTENT, (int) ssl_error_want_write,
> @@ -993,18 +1066,37 @@ SSLInitServerContext(
>   // disable selected protocols
>   SSL_CTX_set_options(ctx, params->ssl_ctx_options);
> 
> +  Debug("ssl.session_cache", "ssl context=%p: using session cache options, enabled=%d, size=%d, num_buckets=%d, skip_on_contention=%d, timeout=%d",
> +		  ctx, params->ssl_session_cache, params->ssl_session_cache_size, params->ssl_session_cache_num_buckets,
> +		  params->ssl_session_cache_skip_on_contention, params->ssl_session_cache_timeout);
> +
>   if (params->ssl_session_cache_timeout) {
> -        SSL_CTX_set_timeout(ctx, params->ssl_session_cache_timeout);
> +    SSL_CTX_set_timeout(ctx, params->ssl_session_cache_timeout);
>   }
> 
>   switch (params->ssl_session_cache) {
>   case SSLConfigParams::SSL_SESSION_CACHE_MODE_OFF:
> -    SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF|SSL_SESS_CACHE_NO_INTERNAL);
> +    Debug("ssl.session_cache", "disabling SSL session cache");
> +
> +    SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF | SSL_SESS_CACHE_NO_INTERNAL);
>     break;
> -  case SSLConfigParams::SSL_SESSION_CACHE_MODE_SERVER:
> +  case SSLConfigParams::SSL_SESSION_CACHE_MODE_SERVER_OPENSSL_IMPL:
> +	Debug("ssl.session_cache", "enabling SSL session cache with OpenSSL implementation");
> +
>     SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
>     SSL_CTX_sess_set_cache_size(ctx, params->ssl_session_cache_size);
>     break;
> +  case SSLConfigParams::SSL_SESSION_CACHE_MODE_SERVER_ATS_IMPL: {
> +    Debug("ssl.session_cache", "enabling SSL session cache with ATS implementation");
> +    /* Add all the OpenSSL callbacks */
> +    SSL_CTX_sess_set_new_cb(ctx, ssl_new_cached_session);
> +    SSL_CTX_sess_set_remove_cb(ctx, ssl_rm_cached_session);
> +    SSL_CTX_sess_set_get_cb(ctx, ssl_get_cached_session);
> +
> +    SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL);
> +
> +    break;
> +    }
>   }
> 
> #ifdef SSL_MODE_RELEASE_BUFFERS
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/lib/ts/ink_mutex.h
> ----------------------------------------------------------------------
> diff --git a/lib/ts/ink_mutex.h b/lib/ts/ink_mutex.h
> index 326d6bb..1de3e9a 100644
> --- a/lib/ts/ink_mutex.h
> +++ b/lib/ts/ink_mutex.h
> @@ -127,4 +127,33 @@ private:
>   ink_mutex& mtx;
> };
> 
> +struct ink_scoped_try_mutex
> +{
> +  explicit ink_scoped_try_mutex(ink_mutex& m) : mtx(m), has_lock(false) {
> +    if(ink_mutex_try_acquire(&mtx)) {
> +      has_lock = true;
> +    }
> +  }
> +
> +  void lock() {
> +    if (!has_lock)
> +      ink_mutex_acquire(&mtx);
> +    has_lock = true;
> +  }
> +
> +  bool hasLock() const {
> +    return has_lock;
> +  }
> +
> +  ~ink_scoped_try_mutex() {
> +    if (has_lock)
> +      ink_mutex_release(&mtx);
> +  }
> +
> +private:
> +  ink_mutex& mtx;
> +  bool has_lock;
> +};
> +
> +
> #endif /* _ink_mutex_h_ */
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/mgmt/RecordsConfig.cc
> ----------------------------------------------------------------------
> diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
> index 6455831..f042037 100644
> --- a/mgmt/RecordsConfig.cc
> +++ b/mgmt/RecordsConfig.cc
> @@ -1286,9 +1286,13 @@ RecordElement RecordsConfig[] = {
>   ,
>   {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.path", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
>   ,
> -  {RECT_CONFIG, "proxy.config.ssl.session_cache", RECD_INT, "1", RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
> +  {RECT_CONFIG, "proxy.config.ssl.session_cache", RECD_INT, "2", RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
>   ,
> -  {RECT_CONFIG, "proxy.config.ssl.session_cache.size", RECD_INT, "20480", RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
> +  {RECT_CONFIG, "proxy.config.ssl.session_cache.size", RECD_INT, "102400", RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
> +  ,
> +  {RECT_CONFIG, "proxy.config.ssl.session_cache.num_buckets", RECD_INT, "256", RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
> +  ,
> +  {RECT_CONFIG, "proxy.config.ssl.session_cache.skip_cache_on_bucket_contention", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_NULL, NULL, RECA_NULL}
>   ,
>   {RECT_CONFIG, "proxy.config.ssl.max_record_size", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_NULL, NULL, RECA_NULL}
>   ,
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/53bf5d1e/proxy/Makefile.am
> ----------------------------------------------------------------------
> diff --git a/proxy/Makefile.am b/proxy/Makefile.am
> index dc5c2a3..6a659b1 100644
> --- a/proxy/Makefile.am
> +++ b/proxy/Makefile.am
> @@ -213,11 +213,11 @@ traffic_server_LDADD = \
>   $(top_builddir)/iocore/cluster/libinkcluster.a \
>   $(top_builddir)/iocore/cache/libinkcache.a \
>   $(top_builddir)/iocore/aio/libinkaio.a \
> +  $(top_builddir)/lib/ts/libtsutil.la \
>   $(top_builddir)/iocore/net/libinknet.a \
>   $(top_builddir)/iocore/eventsystem/libinkevent.a \
>   $(top_builddir)/lib/records/librecords_p.a \
>   $(top_builddir)/iocore/eventsystem/libinkevent.a \
> -  $(top_builddir)/lib/ts/libtsutil.la \
>   @HWLOC_LIBS@ \
>   @LIBPCRE@ \
>   @OPENSSL_LIBS@ \
>