trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bri...@apache.org
Subject [1/3] git commit: TS-3080: Optimized SSL Session Cache
Date Wed, 08 Oct 2014 18:34:02 GMT
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 <briang@apache.org>
Authored: Tue Oct 7 18:51:34 2014 -0700
Committer: Brian Geffon <briang@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@ \


Mime
View raw message