This patch implement SSL session cache sharing across SMP workers using
shared memory. The following new squid configuration options added:

 - The "sslproxy_session_cache_size" option which sets the cache size to
use for ssl session. Example usage:
     sslproxy_session_cache_size 4 MB

 - The "sslproxy_session_ttl" option which defines the time in seconds
the ssl session is valid. Example usage:
     sslproxy_session_ttl  600

This patch also investigates the Ipc::MemMap class to help squid
developers implement shared caches for squid processes.

This is a Measurement Factory project
SMP SSL session cache implementation

This patch implement SSL session cache sharing across SMP workers using shared 
memory. The following new squid configuration options added:

 - The "sslproxy_session_cache_size" option which sets the cache size to use 
   for ssl session. Example usage:
     sslproxy_session_cache_size 4 MB

 - The "sslproxy_session_ttl" option which defines the time in seconds the
   ssl session is valid. Example usage:
     sslproxy_session_ttl  600

This is a Measurement Factory project


=== modified file 'src/Makefile.am'
--- src/Makefile.am	2014-01-03 10:32:53 +0000
+++ src/Makefile.am	2014-01-08 11:03:19 +0000
@@ -610,58 +610,58 @@
 	swap_log_op.cc
 
 CLEANFILES += $(BUILT_SOURCES)
 
 nodist_squid_SOURCES = \
 	$(DISKIO_GEN_SOURCE) \
 	$(BUILT_SOURCES)
 
 squid_LDADD = \
 	$(AUTH_ACL_LIBS) \
 	ident/libident.la \
 	acl/libacls.la \
 	acl/libstate.la \
 	$(AUTH_LIBS) \
 	$(DISK_LIBS) \
 	acl/libapi.la \
 	base/libbase.la \
 	libsquid.la \
 	ip/libip.la \
 	fs/libfs.la \
+	$(SSL_LIBS) \
 	ipc/libipc.la \
 	mgr/libmgr.la \
 	anyp/libanyp.la \
 	comm/libcomm.la \
 	eui/libeui.la \
 	http/libsquid-http.la \
 	icmp/libicmp.la icmp/libicmp-core.la \
 	log/liblog.la \
 	format/libformat.la \
 	$(XTRA_OBJS) \
 	$(DISK_LINKOBJS) \
 	$(REPL_OBJS) \
 	$(DISK_OS_LIBS) \
 	$(CRYPTLIB) \
 	$(REGEXLIB) \
 	$(ADAPTATION_LIBS) \
 	$(ESI_LIBS) \
-	$(SSL_LIBS) \
 	$(SNMP_LIBS) \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(SSLLIB) \
 	$(EPOLL_LIBS) \
 	$(MINGW_LIBS) \
 	$(KRB5LIBS) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 squid_DEPENDENCIES = \
 	$(DISK_LIBS) \
 	$(DISK_LINKOBJS) \
 	$(REPL_OBJS) \
 	$(ADAPTATION_LIBS) \
 	$(ESI_LOCAL_LIBS) \
 	$(SSL_LIBS) \
 	$(AUTH_ACL_LIBS) \
 	ident/libident.la \
 	acl/libacls.la \
@@ -1776,43 +1776,43 @@
 	swap_log_op.cc
 tests_testDiskIO_LDADD = \
 	http/libsquid-http.la \
 	SquidConfig.o \
 	CommCalls.o \
 	DnsLookupDetails.o \
 	ident/libident.la \
 	acl/libacls.la \
 	acl/libstate.la \
 	libsquid.la \
 	comm/libcomm.la \
 	anyp/libanyp.la \
 	ip/libip.la \
 	fs/libfs.la \
 	ipc/libipc.la \
 	$(REPL_OBJS) \
 	$(DISK_LIBS) \
 	$(DISK_OS_LIBS) \
 	acl/libapi.la \
 	mgr/libmgr.la \
+	$(SSL_LIBS) \
 	ipc/libipc.la \
 	base/libbase.la \
-	$(SSL_LIBS) \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(REGEXLIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SSLLIB) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 
 tests_testDiskIO_LDFLAGS = $(LIBADD_DL)
 tests_testDiskIO_DEPENDENCIES = \
 	$(DISK_LIBS) \
 	$(SWAP_TEST_DS) \
 	$(SQUID_CPPUNIT_LA)
 
 ## Tests of the Even module.
 tests_testEvent_SOURCES = \
 	AccessLogEntry.cc \
 	BodyPipe.cc \
 	CacheDigest.h \
@@ -2790,54 +2790,54 @@
 	urn.h \
 	urn.cc \
 	wccp2.h \
 	tests/stub_wccp2.cc \
 	whois.h \
 	tests/stub_whois.cc \
 	FadingCounter.cc \
 	$(WIN32_SOURCE) \
 	wordlist.h \
 	wordlist.cc
 nodist_tests_testHttpRequest_SOURCES = \
 	$(BUILT_SOURCES)
 tests_testHttpRequest_LDADD = \
 	ident/libident.la \
 	acl/libacls.la \
 	acl/libstate.la \
 	acl/libapi.la \
 	libsquid.la \
 	ip/libip.la \
 	fs/libfs.la \
+	$(SSL_LIBS) \
 	ipc/libipc.la \
 	base/libbase.la \
 	mgr/libmgr.la \
 	anyp/libanyp.la \
 	$(SNMP_LIBS) \
 	icmp/libicmp.la icmp/libicmp-core.la \
 	comm/libcomm.la \
 	log/liblog.la \
 	format/libformat.la \
 	http/libsquid-http.la \
 	$(REPL_OBJS) \
 	$(ADAPTATION_LIBS) \
 	$(ESI_LIBS) \
-	$(SSL_LIBS) \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(DISK_OS_LIBS) \
 	$(REGEXLIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SQUID_CPPUNIT_LA) \
 	$(SSLLIB) \
 	$(KRB5LIBS) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testHttpRequest_LDFLAGS = $(LIBADD_DL)
 tests_testHttpRequest_DEPENDENCIES = \
 	$(REPL_OBJS) \
 	$(SQUID_CPPUNIT_LA)
 
 ## why so many sources? well httpHeaderTools requites ACLChecklist & friends.
 ## first line - what we are testing.
 tests_testStore_SOURCES= \
 	CacheDigest.h \
@@ -2977,43 +2977,43 @@
 	wordlist.h \
 	wordlist.cc
 
 nodist_tests_testStore_SOURCES= \
 	$(TESTSOURCES) \
 	SquidMath.cc \
 	SquidMath.h \
 	swap_log_op.cc
 
 tests_testStore_LDADD= \
 	http/libsquid-http.la \
 	ident/libident.la \
 	acl/libacls.la \
 	acl/libstate.la \
 	acl/libapi.la \
 	base/libbase.la \
 	libsquid.la \
 	ip/libip.la \
 	fs/libfs.la \
 	mgr/libmgr.la \
+	$(SSL_LIBS) \
 	ipc/libipc.la \
 	anyp/libanyp.la \
-	$(SSL_LIBS) \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(REGEXLIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SSLLIB) \
 	CommCalls.o \
 	DnsLookupDetails.o \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testStore_LDFLAGS = $(LIBADD_DL)
 tests_testStore_DEPENDENCIES = \
 	$(SQUID_CPPUNIT_LA)
 
 ## string needs mem.cc.
 ## mem.cc needs ClientInfo.h
 ## libsquid pulls in SquidConfig and children. stub them.
 tests_testString_SOURCES = \
 	ClientInfo.h \
 	Mem.h \
@@ -3213,42 +3213,42 @@
 	SquidMath.cc \
 	SquidMath.h \
 	swap_log_op.cc
 tests_testUfs_LDADD = \
 	http/libsquid-http.la \
 	CommCalls.o \
 	DnsLookupDetails.o \
 	ident/libident.la \
 	acl/libacls.la \
 	acl/libstate.la \
 	acl/libapi.la \
 	libsquid.la \
 	ip/libip.la \
 	fs/libfs.la \
 	mgr/libmgr.la \
 	$(REPL_OBJS) \
 	acl/libacls.la \
 	$(DISK_LIBS) \
 	$(DISK_OS_LIBS) \
 	acl/libapi.la \
-	ipc/libipc.la \
 	$(SSL_LIBS) \
+	ipc/libipc.la \
 	comm/libcomm.la \
 	anyp/libanyp.la \
 	base/libbase.la \
 	ip/libip.la \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(REGEXLIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SSLLIB) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testUfs_LDFLAGS = $(LIBADD_DL)
 tests_testUfs_DEPENDENCIES = \
 	$(SWAP_TEST_DS)
 
 check_PROGRAMS += testRefCount
 testRefCount_SOURCES= \
 	base/Lock.h \
 	base/RefCount.h \
@@ -3394,43 +3394,44 @@
 nodist_tests_testRock_SOURCES = \
 	$(DISKIO_GEN_SOURCE) \
 	swap_log_op.cc \
 	SquidMath.cc \
 	SquidMath.h \
 	$(TESTSOURCES)
 tests_testRock_LDADD = \
 	http/libsquid-http.la \
 	libsquid.la \
 	comm/libcomm.la \
 	anyp/libanyp.la \
 	ip/libip.la \
 	fs/libfs.la \
 	$(COMMON_LIBS) \
 	$(REPL_OBJS) \
 	$(DISK_LIBS) \
 	$(DISK_OS_LIBS) \
 	acl/libacls.la \
 	acl/libapi.la \
 	acl/libstate.la \
+	eui/libeui.la \
+	$(SSL_LIBS) \
 	ipc/libipc.la \
 	base/libbase.la \
-	$(SSL_LIBS) \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(REGEXLIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SSLLIB) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testRock_LDFLAGS = $(INCLUDES) $(LIBADD_DL)
 tests_testRock_DEPENDENCIES = \
 	$(SWAP_TEST_DS)
 
 ## Tests of the URL module.
 ## TODO: Trim this down once the insanity is over.
 tests_testURL_SOURCES = \
 	AccessLogEntry.cc \
 	BodyPipe.cc \
 	cache_cf.h \
 	AuthReg.h \
 	YesNoNone.h \
@@ -3630,53 +3631,53 @@
 	whois.h \
 	tests/stub_whois.cc \
 	FadingCounter.cc \
 	$(WIN32_SOURCE) \
 	wordlist.h \
 	wordlist.cc
 nodist_tests_testURL_SOURCES = \
 	$(BUILT_SOURCES)
 tests_testURL_LDADD = \
 	http/libsquid-http.la \
 	anyp/libanyp.la \
 	ident/libident.la \
 	acl/libacls.la \
 	eui/libeui.la \
 	acl/libstate.la \
 	acl/libapi.la \
 	base/libbase.la \
 	libsquid.la \
 	ip/libip.la \
 	fs/libfs.la \
+	$(SSL_LIBS) \
 	ipc/libipc.la \
 	mgr/libmgr.la \
 	$(SNMP_LIBS) \
 	icmp/libicmp.la icmp/libicmp-core.la \
 	comm/libcomm.la \
 	log/liblog.la \
 	$(DISK_OS_LIBS) \
 	format/libformat.la \
 	$(REGEXLIB) \
 	$(REPL_OBJS) \
 	$(ADAPTATION_LIBS) \
 	$(ESI_LIBS) \
-	$(SSL_LIBS) \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(COMPAT_LIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SQUID_CPPUNIT_LA) \
 	$(SSLLIB) \
 	$(KRB5LIBS) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testURL_LDFLAGS = $(LIBADD_DL)
 tests_testURL_DEPENDENCIES = \
 	$(REPL_OBJS) \
 	$(SQUID_CPPUNIT_LA)
 
 tests_testSBuf_SOURCES= \
 	tests/testSBuf.h \
 	tests/testSBuf.cc \
 	tests/testMain.cc \
 	tests/SBufFindTest.h \

=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h	2014-01-01 19:20:49 +0000
+++ src/SquidConfig.h	2014-01-08 11:01:04 +0000
@@ -475,40 +475,42 @@
     HeaderWithAclList *request_header_add;
     ///note
     Notes notes;
     char *coredump_dir;
     char *chroot_dir;
 #if USE_CACHE_DIGESTS
 
     struct {
         int bits_per_entry;
         time_t rebuild_period;
         time_t rewrite_period;
         size_t swapout_chunk_size;
         int rebuild_chunk_percentage;
     } digest;
 #endif
 #if USE_SSL
 
     struct {
         int unclean_shutdown;
         char *ssl_engine;
+        int session_ttl;
+        size_t sessionCacheSize;
     } SSL;
 #endif
 
     wordlist *ext_methods;
 
     struct {
         int high_rptm;
         int high_pf;
         size_t high_memory;
     } warnings;
     char *store_dir_select_algorithm;
     int sleep_after_fork;   /* microseconds */
     time_t minimum_expiry_time; /* seconds */
     external_acl *externalAclHelperList;
 
 #if USE_SSL
 
     struct {
         char *cert;
         char *key;

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2014-01-05 02:56:31 +0000
+++ src/cf.data.pre	2014-01-08 11:01:04 +0000
@@ -2359,40 +2359,58 @@
 NAME: sslproxy_cafile
 IFDEF: USE_SSL
 DEFAULT: none
 LOC: Config.ssl_client.cafile
 TYPE: string
 DOC_START
 	file containing CA certificates to use when verifying server
 	certificates while proxying https:// URLs
 DOC_END
 
 NAME: sslproxy_capath
 IFDEF: USE_SSL
 DEFAULT: none
 LOC: Config.ssl_client.capath
 TYPE: string
 DOC_START
 	directory containing CA certificates to use when verifying
 	server certificates while proxying https:// URLs
 DOC_END
 
+NAME: sslproxy_session_ttl
+IFDEF: USE_SSL
+DEFAULT: 300
+LOC: Config.SSL.session_ttl
+TYPE: int
+DOC_START
+	Sets the timeout value for SSL sessions
+DOC_END
+
+NAME: sslproxy_session_cache_size
+IFDEF: USE_SSL
+DEFAULT: 2 MB
+LOC: Config.SSL.sessionCacheSize
+TYPE: b_size_t
+DOC_START
+        Sets the cache size to use for ssl session
+DOC_END
+
 NAME: ssl_bump
 IFDEF: USE_SSL
 TYPE: sslproxy_ssl_bump
 LOC: Config.accessList.ssl_bump
 DEFAULT_DOC: Does not bump unless rules are present in squid.conf
 DEFAULT: none
 DOC_START
 	This option is consulted when a CONNECT request is received on
 	an http_port (or a new connection is intercepted at an
 	https_port), provided that port was configured with an ssl-bump
 	flag. The subsequent data on the connection is either treated as
 	HTTPS and decrypted OR tunneled at TCP level without decryption,
 	depending on the first bumping "mode" which ACLs match.
 
 	ssl_bump <mode> [!]acl ...
 
 	The following bumping modes are supported:
 
 	    client-first
 		Allow bumping of the connection. Establish a secure connection

=== modified file 'src/ipc/Makefile.am'
--- src/ipc/Makefile.am	2013-01-17 04:25:35 +0000
+++ src/ipc/Makefile.am	2014-01-08 10:17:42 +0000
@@ -1,35 +1,37 @@
 include $(top_srcdir)/src/Common.am
 include $(top_srcdir)/src/TestHeaders.am
 
 noinst_LTLIBRARIES = libipc.la
 
 libipc_la_SOURCES = \
 	AtomicWord.cc \
 	AtomicWord.h \
 	FdNotes.cc \
 	FdNotes.h \
 	Kid.cc \
 	Kid.h \
 	Kids.cc \
 	Kids.h \
 	Messages.h \
+	MemMap.cc \
+	MemMap.h \
 	Queue.cc \
 	Queue.h \
 	ReadWriteLock.cc \
 	ReadWriteLock.h \
 	StartListening.cc \
 	StartListening.h \
 	StoreMap.cc \
 	StoreMap.h \
 	StrandCoord.cc \
 	StrandCoord.h \
 	StrandCoords.h \
 	StrandSearch.cc \
 	StrandSearch.h \
 	SharedListen.cc \
 	SharedListen.h \
 	TypedMsgHdr.cc \
 	TypedMsgHdr.h \
 	Coordinator.cc \
 	Coordinator.h \
 	UdsOp.cc \

=== added file 'src/ipc/MemMap.cc'
--- src/ipc/MemMap.cc	1970-01-01 00:00:00 +0000
+++ src/ipc/MemMap.cc	2014-01-08 10:17:42 +0000
@@ -0,0 +1,350 @@
+/*
+ * DEBUG: section 54    Interprocess Communication
+ */
+
+#include "squid.h"
+#include "ipc/MemMap.h"
+#include "store_key_md5.h"
+#include "tools.h"
+
+Ipc::MemMap::MemMap(const char *const aPath): cleaner(NULL), path(aPath),
+                    shared(shm_old(Shared)(aPath))
+{
+    assert(shared->limit > 0); // we should not be created otherwise
+    debugs(54, 5, HERE << "attached map [" << path << "] created: " <<
+           shared->limit);
+}
+
+Ipc::MemMap::Owner *
+Ipc::MemMap::Init(const char *const path, const int limit, const size_t extrasSize)
+{
+    assert(limit > 0); // we should not be created otherwise
+    Owner *const owner = shm_new(Shared)(path, limit, extrasSize);
+    debugs(54, 5, HERE << "new map [" << path << "] created: " << limit);
+    return owner;
+}
+
+Ipc::MemMap::Owner *
+Ipc::MemMap::Init(const char *const path, const int limit)
+{
+    return Init(path, limit, 0);
+}
+
+
+Ipc::MemMap::Slot *
+Ipc::MemMap::openForWriting(const cache_key *const key, sfileno &fileno)
+{
+    Slot *slots = shared->slots();
+
+    debugs(54, 5, HERE << " trying to open slot for key " << storeKeyText(key)
+           << " for writing in map [" << path << ']');
+    const int idx = slotIndexByKey(key);
+
+    Slot &s = slots[idx];
+    ReadWriteLock &lock = s.lock;
+
+    if (lock.lockExclusive()) {
+        assert(s.state != Slot::Writeable); // until we start breaking locks
+
+        // free if the entry was used, keeping the entry locked
+        if (s.waitingToBeFreed || s.state == Slot::Readable)
+            freeLocked(s, true);
+
+        assert(s.state == Slot::Empty);
+        ++shared->count;
+        s.state = Slot::Writeable;
+        fileno = idx;
+        //s.setKey(key); // XXX: the caller should do that
+        debugs(54, 5, HERE << " opened slot at " << idx <<
+               " for writing in map [" << path << ']');
+        return &s; // and keep the entry locked
+    }
+
+    debugs(54, 5, HERE << " failed to open slot at " << idx <<
+           " for writing in map [" << path << ']');
+    return NULL;
+}
+
+void
+Ipc::MemMap::closeForWriting(const sfileno fileno, bool lockForReading)
+{
+    debugs(54, 5, HERE << " closing slot at " << fileno << " for writing and "
+           "openning for reading in map [" << path << ']');
+    assert(valid(fileno));
+    Slot *slots = shared->slots();
+    Slot &s = slots[fileno];
+    assert(s.state == Slot::Writeable);
+    s.state = Slot::Readable;
+    if (lockForReading)
+        s.lock.switchExclusiveToShared();
+    else
+        s.lock.unlockExclusive();
+}
+
+/// terminate writing the entry, freeing its slot for others to use
+void
+Ipc::MemMap::abortWriting(const sfileno fileno)
+{
+    debugs(54, 5, HERE << " abort writing slot at " << fileno <<
+           " in map [" << path << ']');
+    assert(valid(fileno));
+    Slot *slots = shared->slots();
+    Slot &s = slots[fileno];
+    assert(s.state == Slot::Writeable);
+    freeLocked(s, false);
+}
+
+void
+Ipc::MemMap::abortIo(const sfileno fileno)
+{
+    debugs(54, 5, HERE << " abort I/O for slot at " << fileno <<
+           " in map [" << path << ']');
+    assert(valid(fileno));
+    Slot *slots = shared->slots();
+    Slot &s = slots[fileno];
+
+    // The caller is a lock holder. Thus, if we are Writeable, then the
+    // caller must be the writer; otherwise the caller must be the reader.
+    if (s.state == Slot::Writeable)
+        abortWriting(fileno);
+    else
+        closeForReading(fileno);
+}
+
+const Ipc::MemMap::Slot *
+Ipc::MemMap::peekAtReader(const sfileno fileno) const
+{
+    assert(valid(fileno));
+    Slot *slots = shared->slots();
+    const Slot &s = slots[fileno];
+    switch (s.state) {
+    case Slot::Readable:
+        return &s; // immediate access by lock holder so no locking
+    case Slot::Writeable:
+        return NULL; // cannot read the slot when it is being written
+    case Slot::Empty:
+        assert(false); // must be locked for reading or writing
+    }
+    assert(false); // not reachable
+    return NULL;
+}
+
+void
+Ipc::MemMap::free(const sfileno fileno)
+{
+    debugs(54, 5, HERE << " marking slot at " << fileno << " to be freed in"
+           " map [" << path << ']');
+
+    assert(valid(fileno));
+    Slot *slots = shared->slots();
+    Slot &s = slots[fileno];
+
+    if (s.lock.lockExclusive())
+        freeLocked(s, false);
+    else
+        s.waitingToBeFreed = true; // mark to free it later
+}
+
+const Ipc::MemMap::Slot *
+Ipc::MemMap::openForReading(const cache_key *const key, sfileno &fileno)
+{
+    debugs(54, 5, HERE << " trying to open slot for key " << storeKeyText(key)
+           << " for reading in map [" << path << ']');
+    const int idx = slotIndexByKey(key);
+    if (const Slot *slot = openForReadingAt(idx)) {
+        if (slot->sameKey(key)) {
+            fileno = idx;
+            debugs(54, 5, HERE << " opened slot at " << fileno << " for key "
+                   << storeKeyText(key) << " for reading in map [" << path <<
+                   ']');
+            return slot; // locked for reading
+        }
+        slot->lock.unlockShared();
+    }
+    debugs(54, 5, HERE << " failed to open slot for key " << storeKeyText(key)
+           << " for reading in map [" << path << ']');
+    return NULL;
+}
+
+const Ipc::MemMap::Slot *
+Ipc::MemMap::openForReadingAt(const sfileno fileno)
+{
+    Slot *slots = shared->slots();
+
+    debugs(54, 5, HERE << " trying to open slot at " << fileno << " for "
+           "reading in map [" << path << ']');
+    assert(valid(fileno));
+    Slot &s = slots[fileno];
+
+    if (!s.lock.lockShared()) {
+        debugs(54, 5, HERE << " failed to lock slot at " << fileno << " for "
+               "reading in map [" << path << ']');
+        return NULL;
+    }
+
+    if (s.state == Slot::Empty) {
+        s.lock.unlockShared();
+        debugs(54, 7, HERE << " empty slot at " << fileno << " for "
+               "reading in map [" << path << ']');
+        return NULL;
+    }
+
+    if (s.waitingToBeFreed) {
+        s.lock.unlockShared();
+        debugs(54, 7, HERE << " dirty slot at " << fileno << " for "
+               "reading in map [" << path << ']');
+        return NULL;
+    }
+
+    // cannot be Writing here if we got shared lock and checked Empty above
+    assert(s.state == Slot::Readable);
+    debugs(54, 5, HERE << " opened slot at " << fileno << " for reading in"
+           " map [" << path << ']');
+    return &s;
+}
+
+void
+Ipc::MemMap::closeForReading(const sfileno fileno)
+{
+    debugs(54, 5, HERE << " closing slot at " << fileno << " for reading in "
+           "map [" << path << ']');
+    assert(valid(fileno));
+    Slot *slots = shared->slots();
+    Slot &s = slots[fileno];
+    assert(s.state == Slot::Readable);
+    s.lock.unlockShared();
+}
+
+int
+Ipc::MemMap::entryLimit() const
+{
+    return shared->limit;
+}
+
+int
+Ipc::MemMap::entryCount() const
+{
+    return shared->count;
+}
+
+bool
+Ipc::MemMap::full() const
+{
+    return entryCount() >= entryLimit();
+}
+
+void
+Ipc::MemMap::updateStats(ReadWriteLockStats &stats) const
+{
+    Slot *slots = shared->slots();
+    for (int i = 0; i < shared->limit; ++i)
+        slots[i].lock.updateStats(stats);
+}
+
+bool
+Ipc::MemMap::valid(const int pos) const
+{
+    return 0 <= pos && pos < entryLimit();
+}
+
+static
+unsigned int
+hash_key(const unsigned char *data, unsigned int len, unsigned int hashSize)
+{
+    unsigned int n;
+    unsigned int j;
+    for(j = 0, n = 0; j < len; j++ ) {
+        n ^= 271 * *data;
+        ++data;
+    }
+    return (n ^ (j * 271)) % hashSize;
+}
+
+int
+Ipc::MemMap::slotIndexByKey(const cache_key *const key) const
+{
+    const unsigned char *k = reinterpret_cast<const unsigned char *>(key);
+    return hash_key(k, SSL_SESSION_ID_SIZE, shared->limit);
+}
+
+Ipc::MemMap::Slot &
+Ipc::MemMap::slotByKey(const cache_key *const key)
+{
+    Slot *slots = shared->slots();
+    return slots[slotIndexByKey(key)];
+}
+
+/// unconditionally frees the already exclusively locked slot and releases lock
+void
+Ipc::MemMap::freeLocked(Slot &s, bool keepLocked)
+{
+    Slot *slots = shared->slots();
+    if (s.state == Slot::Readable && cleaner)
+        cleaner->cleanReadable(&s - slots);
+
+    s.waitingToBeFreed = false;
+    s.state = Slot::Empty;
+    if (!keepLocked)
+        s.lock.unlockExclusive();
+    --shared->count;
+    debugs(54, 5, HERE << " freed slot at " << (&s - slots) <<
+           " in map [" << path << ']');
+}
+
+/* Ipc::MemMapSlot */
+Ipc::MemMapSlot::MemMapSlot(): state(Empty)
+{
+    memset(key, 0, sizeof(key));
+    memset(p, 0, sizeof(p));
+    pSize = 0;
+}
+
+void
+Ipc::MemMapSlot::set(const unsigned char *aKey, const void *block, size_t blockSize, time_t expireOn)
+{
+    memcpy(key, aKey, sizeof(key));
+    if (block)
+        memcpy(p, block, blockSize);
+    pSize = blockSize;
+    expire = expireOn;
+}
+
+bool
+Ipc::MemMapSlot::sameKey(const cache_key *const aKey) const
+{
+    return (memcmp(key, aKey, sizeof(key)) == 0);
+}
+
+/* Ipc::MemMap::Shared */
+
+Ipc::MemMap::Shared::Shared(const int aLimit, const size_t anExtrasSize):
+    limit(aLimit), extrasSize(anExtrasSize), count(0)
+{
+    // All of the Slot members should initialized to zero.
+    // XXX: use FlexibleArray instead of this hack
+    memset(slots(), 0, aLimit * sizeof(Shared));
+}
+
+Ipc::MemMap::Shared::~Shared()
+{
+}
+
+size_t
+Ipc::MemMap::Shared::sharedMemorySize() const
+{
+    return SharedMemorySize(limit, extrasSize);
+}
+
+size_t
+Ipc::MemMap::Shared::SharedMemorySize(const int limit, const size_t extrasSize)
+{
+    return sizeof(Shared) + limit * (sizeof(Slot) + extrasSize);
+}
+
+Ipc::MemMap::Slot *
+Ipc::MemMap::Shared::slots()
+{
+    // XXX: use FlexibleArray instead of this hack
+    void *p = this;
+    return (Slot *)((size_t)p + sizeof(Shared));
+}

=== added file 'src/ipc/MemMap.h'
--- src/ipc/MemMap.h	1970-01-01 00:00:00 +0000
+++ src/ipc/MemMap.h	2014-01-08 10:17:42 +0000
@@ -0,0 +1,139 @@
+#ifndef SQUID_IPC_STORE_MAP_H
+#define SQUID_IPC_STORE_MAP_H
+
+#include "Debug.h"
+#include "ipc/ReadWriteLock.h"
+#include "ipc/mem/Pointer.h"
+#include "tools.h"
+#include "typedefs.h"
+
+namespace Ipc
+{
+
+#define SSL_SESSION_ID_SIZE 32
+#define SSL_SESSION_MAX_SIZE 10*1024
+
+/// a MemMap basic element, holding basic shareable memory block info
+class MemMapSlot
+{
+public:
+    mutable ReadWriteLock lock; ///< protects slot data below
+    Atomic::WordT<uint8_t> waitingToBeFreed; ///< may be accessed w/o a lock
+
+    /// possible persistent states
+    typedef enum {
+        Empty, ///< ready for writing, with nothing of value
+        Writeable, ///< transitions from Empty to Readable
+        Readable, ///< ready for reading
+    } State;
+    State state; ///< current state
+
+    unsigned char key[SSL_SESSION_ID_SIZE]; ///< The entry key
+    unsigned char p[SSL_SESSION_MAX_SIZE]; ///< The memory block;
+    size_t pSize;
+    time_t expire;
+
+    MemMapSlot();
+    size_t size() const {return sizeof(MemMapSlot);}
+    size_t keySize() const {return sizeof(key);}
+    bool sameKey(const cache_key *const aKey) const;
+    void set(const unsigned char *aKey, const void *block, size_t blockSize, time_t expire = 0);
+};
+
+class MemMapCleaner;
+
+/// map of MemMapSlots indexed by their keys, with read/write slot locking
+/// kids extend to store custom data
+class MemMap
+{
+public:
+    typedef MemMapSlot Slot;
+
+    /// data shared across maps in different processes
+    class Shared
+    {
+    public:
+        Shared(const int aLimit, const size_t anExtrasSize);
+        size_t sharedMemorySize() const;
+        static size_t SharedMemorySize(const int limit, const size_t anExtrasSize);
+        Slot *slots();
+        ~Shared();
+
+        const int limit; ///< maximum number of map slots
+        const size_t extrasSize; ///< size of slot extra data
+        Atomic::Word count; ///< current number of map slots
+    private:
+        Shared(); //disabled
+        Shared &operator=(const Shared&); //disabled
+        Shared(const Shared&); //disabled
+    };
+
+public:
+    typedef Mem::Owner<Shared> Owner;
+
+    /// initialize shared memory
+    static Owner *Init(const char *const path, const int limit);
+
+    MemMap(const char *const aPath);
+
+    /// finds, reservers space for writing a new entry or returns nil
+    Slot *openForWriting(const cache_key *const key, sfileno &fileno);
+    /// successfully finish writing the entry
+    void closeForWriting(const sfileno fileno, bool lockForReading = false);
+
+    /// only works on locked entries; returns nil unless the slot is readable
+    const Slot *peekAtReader(const sfileno fileno) const;
+
+    /// mark the slot as waiting to be freed and, if possible, free it
+    void free(const sfileno fileno);
+
+    /// open slot for reading, increments read level
+    const Slot *openForReading(const cache_key *const key, sfileno &fileno);
+    /// open slot for reading, increments read level
+    const Slot *openForReadingAt(const sfileno fileno);
+    /// close slot after reading, decrements read level
+    void closeForReading(const sfileno fileno);
+
+    /// called by lock holder to terminate either slot writing or reading
+    void abortIo(const sfileno fileno);
+
+    bool full() const; ///< there are no empty slots left
+    bool valid(const int n) const; ///< whether n is a valid slot coordinate
+    int entryCount() const; ///< number of used slots
+    int entryLimit() const; ///< maximum number of slots that can be used
+
+    /// adds approximate current stats to the supplied ones
+    void updateStats(ReadWriteLockStats &stats) const;
+
+    MemMapCleaner *cleaner; ///< notified before a readable entry is freed
+
+protected:
+    static Owner *Init(const char *const path, const int limit, const size_t extrasSize);
+
+    const String path; ///< cache_dir path, used for logging
+    Mem::Pointer<Shared> shared;
+    int ttl;
+
+private:
+    int slotIndexByKey(const cache_key *const key) const;
+    Slot &slotByKey(const cache_key *const key);
+
+    Slot *openForReading(Slot &s);
+    void abortWriting(const sfileno fileno);
+    void freeIfNeeded(Slot &s);
+    void freeLocked(Slot &s, bool keepLocked);
+};
+
+/// API for adjusting external state when dirty map slot is being freed
+class MemMapCleaner
+{
+public:
+    virtual ~MemMapCleaner() {}
+
+    /// adjust slot-linked state before a locked Readable slot is erased
+    virtual void cleanReadable(const sfileno fileno) = 0;
+};
+
+} // namespace Ipc
+
+#endif /* SQUID_IPC_STORE_MAP_H */

=== modified file 'src/main.cc'
--- src/main.cc	2013-11-29 19:47:54 +0000
+++ src/main.cc	2014-01-08 11:01:04 +0000
@@ -1038,40 +1038,44 @@
     if (WIN32_OS_version > _WIN_OS_WINNT) {
         WIN32_IpAddrChangeMonitorInit();
     }
 
 #endif
 
     if (!configured_once)
         disk_init();		/* disk_init must go before ipcache_init() */
 
     ipcache_init();
 
     fqdncache_init();
 
     parseEtcHosts();
 
     dnsInit();
 
 #if USE_SSL_CRTD
     Ssl::Helper::GetInstance()->Init();
 #endif
+#if USE_SSL
+    if (!configured_once)
+        Ssl::initialize_session_cache();
+#endif
 
 #if USE_SSL
     if (Ssl::CertValidationHelper::GetInstance())
         Ssl::CertValidationHelper::GetInstance()->Init();
 #endif
 
     redirectInit();
 #if USE_AUTH
     authenticateInit(&Auth::TheConfig);
 #endif
     externalAclInit();
 
     httpHeaderInitModule();	/* must go before any header processing (e.g. the one in errorInitialize) */
 
     httpReplyInitModule();	/* must go before accepting replies */
 
     errorInitialize();
 
     accessLogInit();
 

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2013-10-25 00:13:46 +0000
+++ src/ssl/support.cc	2014-01-08 11:05:21 +0000
@@ -24,52 +24,58 @@
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
 
 #include "squid.h"
 
 /* MS Visual Studio Projects are monolithic, so we need the following
  * #if to exclude the SSL code from compile process when not needed.
  */
 #if USE_SSL
 
 #include "acl/FilledChecklist.h"
 #include "anyp/PortCfg.h"
 #include "fde.h"
+#include "ipc/MemMap.h"
 #include "globals.h"
 #include "SquidConfig.h"
+#include "SquidTime.h"
 #include "ssl/Config.h"
 #include "ssl/ErrorDetail.h"
 #include "ssl/gadgets.h"
 #include "ssl/support.h"
 #include "URL.h"
 
 #if HAVE_ERRNO_H
 #include <errno.h>
 #endif
 
+static void setSessionCallbacks(SSL_CTX *ctx);
+Ipc::MemMap *SslSessionCache = NULL;
+const char *SslSessionCacheName = "ssl_session_cache";
+
 const char *Ssl::BumpModeStr[] = {
     "none",
     "client-first",
     "server-first",
     NULL
 };
 
 /**
  \defgroup ServerProtocolSSLInternal Server-Side SSL Internals
  \ingroup ServerProtocolSSLAPI
  */
 
 /// \ingroup ServerProtocolSSLInternal
 static int
 ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata)
 {
     FILE *in;
     int len = 0;
     char cmdline[1024];
 
@@ -716,41 +722,40 @@
         if (Config.SSL.ssl_engine) {
             ENGINE *e;
 
             if (!(e = ENGINE_by_id(Config.SSL.ssl_engine))) {
                 fatalf("Unable to find SSL engine '%s'\n", Config.SSL.ssl_engine);
             }
 
             if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
                 int ssl_error = ERR_get_error();
                 fatalf("Failed to initialise SSL engine: %s\n",
                        ERR_error_string(ssl_error, NULL));
             }
         }
 
 #else
         if (Config.SSL.ssl_engine) {
             fatalf("Your OpenSSL has no SSL engine support\n");
         }
 
 #endif
-
     }
 
     ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL);
     ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL);
     ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist);
     ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail);
     ssl_ex_index_ssl_peeked_cert  = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509);
     ssl_ex_index_ssl_errors =  SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors);
     ssl_ex_index_ssl_cert_chain = SSL_get_ex_new_index(0, (void *) "ssl_cert_chain", NULL, NULL, &ssl_free_CertChain);
     ssl_ex_index_ssl_validation_counter = SSL_get_ex_new_index(0, (void *) "ssl_validation_counter", NULL, NULL, &ssl_free_int);
 }
 
 /// \ingroup ServerProtocolSSLInternal
 static int
 ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile)
 {
     X509_STORE *st = SSL_CTX_get_cert_store(sslContext);
     X509_CRL *crl;
     BIO *in = BIO_new_file(CRLfile, "r");
     int count = 0;
@@ -897,40 +902,42 @@
 
 #if X509_V_FLAG_CRL_CHECK
         if (port.sslContextFlags & SSL_FLAG_VERIFY_CRL_ALL)
             X509_STORE_set_flags(SSL_CTX_get_cert_store(sslContext), X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
         else if (port.sslContextFlags & SSL_FLAG_VERIFY_CRL)
             X509_STORE_set_flags(SSL_CTX_get_cert_store(sslContext), X509_V_FLAG_CRL_CHECK);
 #endif
 
     } else {
         debugs(83, 9, "Not requiring any client certificates");
         SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
     }
 
     if (port.dhParams.get()) {
         SSL_CTX_set_tmp_dh(sslContext, port.dhParams.get());
     }
 
     if (port.sslContextFlags & SSL_FLAG_DONT_VERIFY_DOMAIN)
         SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
 
+    setSessionCallbacks(sslContext);
+
     return true;
 }
 
 SSL_CTX *
 sslCreateServerContext(AnyP::PortCfg &port)
 {
     int ssl_error;
     SSL_CTX *sslContext;
     const char *keyfile, *certfile;
     certfile = port.cert;
     keyfile = port.key;
 
     ssl_initialize();
 
     if (!keyfile)
         keyfile = certfile;
 
     if (!certfile)
         certfile = keyfile;
 
@@ -1656,21 +1663,204 @@
 Ssl::CertError &
 Ssl::CertError::operator = (const CertError &old)
 {
     code = old.code;
     cert.resetAndLock(old.cert.get());
     return *this;
 }
 
 bool
 Ssl::CertError::operator == (const CertError &ce) const
 {
     return code == ce.code && cert.get() == ce.cert.get();
 }
 
 bool
 Ssl::CertError::operator != (const CertError &ce) const
 {
     return code != ce.code || cert.get() != ce.cert.get();
 }
 
+int
+store_session_cb(SSL *ssl, SSL_SESSION *session)
+{
+    if (!SslSessionCache)
+        return 0;
+
+    debugs(83, 5, HERE << "Request to store SSL Session ");
+
+    SSL_SESSION_set_timeout(session, Config.SSL.session_ttl);
+
+    unsigned char *id = session->session_id;
+    unsigned int idlen = session->session_id_length;
+    unsigned char key[SSL_SESSION_ID_SIZE];
+    memset(key, 0, sizeof(key));
+    memcpy(key, id, idlen);
+    int pos;
+    Ipc::MemMap::Slot *slotW = SslSessionCache->openForWriting((const cache_key*)key, pos);
+    if (slotW) {
+        int lenRequired =  i2d_SSL_SESSION(session, NULL);
+        if (lenRequired < SSL_SESSION_MAX_SIZE) {
+            unsigned char *p = (unsigned char *)slotW->p;
+            lenRequired = i2d_SSL_SESSION(session, &p);
+            slotW->set(key, NULL, lenRequired, squid_curtime + Config.SSL.session_ttl);
+        }
+        SslSessionCache->closeForWriting(pos);
+        debugs(83, 5, HERE << " wrote an ssl session entry of size " << lenRequired << " at pos " << pos);
+    }
+    return 0;
+}
+
+void
+remove_session_cb(SSL_CTX *, SSL_SESSION *sessionID)
+{
+    if (!SslSessionCache)
+        return ;
+
+    debugs(83, 5, HERE << "Request to remove corrupted or not valid SSL Session ");
+    int pos;
+    Ipc::MemMap::Slot const *slot = SslSessionCache->openForReading((const cache_key*)sessionID, pos);
+    if (slot == NULL)
+        return;
+    SslSessionCache->closeForReading(pos);
+    // What if we are not able to remove the session?
+    // Maybe schedule a job to remove it later?
+    // For now we just have an invalid entry in cache until will be expired
+    // The opneSSL will reject it when we try to use it
+    SslSessionCache->free(pos);
+}
+
+SSL_SESSION *
+get_session_cb(SSL *, unsigned char *sessionID, int len, int *copy)
+{
+    if (!SslSessionCache)
+        return NULL;
+
+
+    SSL_SESSION *session = NULL;
+    const unsigned int *p;
+    p = (unsigned int *)sessionID;
+    debugs(83, 5, HERE << "Request to search for SSL Session of len:" <<
+           len << p[0] << ":" << p[1]);
+
+    int pos;
+    Ipc::MemMap::Slot const *slot = SslSessionCache->openForReading((const cache_key*)sessionID, pos);
+    if (slot != NULL) {
+        if (slot->expire > squid_curtime) {
+            const unsigned char *ptr = slot->p;
+            session = d2i_SSL_SESSION(NULL, &ptr, slot->pSize);
+            debugs(83, 5, HERE << "Session retrieved from cache at pos " << pos);
+        } else
+            debugs(83, 5, HERE << "Session in cache expired");
+        SslSessionCache->closeForReading(pos);
+    }
+
+    if (!session)
+        debugs(83, 5, HERE << "Failed to retrieved from cache\n");
+
+    // With the parameter copy the callback can require the SSL engine
+    // to increment the reference count of the SSL_SESSION object, Normally
+    // the reference count is not incremented and therefore the session must
+    // not be explicitly freed with SSL_SESSION_free(3).
+    *copy = 0;
+    return session;
+}
+
+static
+void
+setSessionCallbacks(SSL_CTX *ctx)
+{
+    if (SslSessionCache) {
+        SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL);
+        SSL_CTX_sess_set_new_cb(ctx, store_session_cb);
+        SSL_CTX_sess_set_remove_cb(ctx, remove_session_cb);
+        SSL_CTX_sess_set_get_cb(ctx, get_session_cb);
+    }
+}
+
+static
+bool
+isSslServer()
+{
+    if (Config.Sockaddr.https)
+        return true;
+
+    for (AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) {
+        if (s->flags.tunnelSslBumping)
+            return true;
+    }
+
+    return false;
+}
+
+void
+Ssl::initialize_session_cache()
+{
+
+    if (!isSslServer()) //no need to configure ssl session cache.
+        return;
+
+    int configuredItems = ::Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot);
+    if (IamWorkerProcess() && configuredItems)
+        SslSessionCache = new Ipc::MemMap(SslSessionCacheName);
+    else
+        SslSessionCache = NULL;
+
+    for (AnyP::PortCfg *s = ::Config.Sockaddr.https; s; s = s->next) {
+        if (s->staticSslContext.get() != NULL)
+            setSessionCallbacks(s->staticSslContext.get());
+    }
+
+    for (AnyP::PortCfg *s = ::Config.Sockaddr.http; s; s = s->next) {
+        if (s->staticSslContext.get() != NULL)
+            setSessionCallbacks(s->staticSslContext.get());
+    }
+}
+
+void
+destruct_session_cache()
+{
+    delete SslSessionCache;
+}
+
+/// initializes shared memory segments used by MemStore
+class SharedSessionCacheRr: public Ipc::Mem::RegisteredRunner
+{
+public:
+    /* RegisteredRunner API */
+    SharedSessionCacheRr(): owner(NULL) {}
+    virtual void run(const RunnerRegistry &);
+    virtual ~SharedSessionCacheRr();
+
+protected:
+    virtual void create(const RunnerRegistry &);
+
+private:
+    Ipc::MemMap::Owner *owner;
+};
+
+RunnerRegistrationEntry(rrAfterConfig, SharedSessionCacheRr);
+
+void
+SharedSessionCacheRr::run(const RunnerRegistry &r)
+{
+    Ipc::Mem::RegisteredRunner::run(r);
+}
+
+void
+SharedSessionCacheRr::create(const RunnerRegistry &)
+{
+    if (!isSslServer()) //no need to configure ssl session cache.
+        return;
+
+    int items;
+    items = Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot);
+    if (items)
+        owner =  Ipc::MemMap::Init(SslSessionCacheName, items);
+}
+
+SharedSessionCacheRr::~SharedSessionCacheRr()
+{
+    delete owner;
+}
+
 #endif /* USE_SSL */

=== modified file 'src/ssl/support.h'
--- src/ssl/support.h	2013-07-27 13:37:29 +0000
+++ src/ssl/support.h	2014-01-08 11:01:04 +0000
@@ -260,40 +260,52 @@
  */
 bool checkX509ServerValidity(X509 *cert, const char *server);
 
 /**
    \ingroup ServerProtocolSSLAPI
    * Convert a given ASN1_TIME to a string form.
    \param tm the time in ASN1_TIME form
    \param buf the buffer to write the output
    \param len write at most len bytes
    \return The number of bytes written
  */
 int asn1timeToString(ASN1_TIME *tm, char *buf, int len);
 
 /**
    \ingroup ServerProtocolSSLAPI
    * Sets the hostname for the Server Name Indication (SNI) TLS extension
    * if supported by the used openssl toolkit.
    \return true if SNI set false otherwise
 */
 bool setClientSNI(SSL *ssl, const char *fqdn);
+
+/**
+   \ingroup ServerProtocolSSLAPI
+   * Initializes the shared session cache if configured
+*/
+void initialize_session_cache();
+
+/**
+   \ingroup ServerProtocolSSLAPI
+   * Destroy the shared session cache if configured
+*/
+void destruct_session_cache();
 } //namespace Ssl
 
 #if _SQUID_WINDOWS_
 
 #if defined(__cplusplus)
 
 /** \cond AUTODOCS-IGNORE */
 namespace Squid
 {
 /** \endcond */
 
 /// \ingroup ServerProtocolSSLAPI
 inline
 int SSL_set_fd(SSL *ssl, int fd)
 {
     return ::SSL_set_fd(ssl, _get_osfhandle(fd));
 }
 
 /// \ingroup ServerProtocolSSLAPI
 #define SSL_set_fd(ssl,fd) Squid::SSL_set_fd(ssl,fd)

Reply via email to