From 80f461dd9ca3b4ae0bf36491013198f68c4bab32 Mon Sep 17 00:00:00 2001
From: Markus Goetz <Markus.Goetz@nokia.com>
Date: Sat, 13 Nov 2010 14:15:28 +0100
Subject: [PATCH 2/2] SSL: Implement session sharing and use it from QNAM

This improves memory usage and performance since a network round trip
can be avoided.

Reviewed-by: thiago? etc
---
 src/network/access/qhttpnetworkconnection.cpp      |   14 +-
 src/network/access/qhttpnetworkconnection_p.h      |    8 +
 .../access/qhttpnetworkconnectionchannel.cpp       |   11 +
 src/network/access/qhttpnetworkreply.cpp           |    6 +
 src/network/access/qnetworkaccesshttpbackend.cpp   |   19 +-
 src/network/ssl/qsslconfiguration.h                |    1 +
 src/network/ssl/qsslcontext.cpp                    |  259 ++++++++++++++++++++
 src/network/ssl/qsslcontext_p.h                    |  126 ++++++++++
 src/network/ssl/qsslsocket.cpp                     |    6 +
 src/network/ssl/qsslsocket_openssl.cpp             |  199 ++++------------
 src/network/ssl/qsslsocket_openssl_p.h             |    5 +-
 src/network/ssl/qsslsocket_openssl_symbols.cpp     |   22 ++
 src/network/ssl/qsslsocket_openssl_symbols_p.h     |   22 ++
 src/network/ssl/qsslsocket_p.h                     |    5 +
 src/network/ssl/ssl.pri                            |    2 +
 tests/auto/qsslsocket/tst_qsslsocket.cpp           |   30 ++--
 16 files changed, 551 insertions(+), 184 deletions(-)
 create mode 100644 src/network/ssl/qsslcontext.cpp
 create mode 100644 src/network/ssl/qsslcontext_p.h

diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 4d27531..c70b4b7 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -903,9 +903,17 @@ void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config
     if (!d->encrypt)
         return;
 
-    // set the config on all channels
-    for (int i = 0; i < d->channelCount; ++i)
-        static_cast<QSslSocket *>(d->channels[i].socket)->setSslConfiguration(config);
+    if (d->sslContext.isNull()) {
+        d->sslContext = QSharedPointer<QSslContext>(QSslContext::fromConfiguration(true, config));
+    } else {
+        qWarning() << "QHttpNetworkConnection::setSslConfiguration() is only allowed to be called once per object";
+    }
+}
+
+QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext()
+{
+    Q_D(QHttpNetworkConnection);
+    return d->sslContext;
 }
 
 void QHttpNetworkConnection::ignoreSslErrors(int channel)
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 8461426c..f2da0ca 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -60,6 +60,7 @@
 #include <qauthenticator.h>
 #include <qnetworkproxy.h>
 #include <qbuffer.h>
+#include <qsharedpointer.h>
 
 #include <private/qhttpnetworkheader_p.h>
 #include <private/qhttpnetworkrequest_p.h>
@@ -70,6 +71,8 @@
 #ifndef QT_NO_HTTP
 
 #ifndef QT_NO_OPENSSL
+#    include <private/qsslcontext_p.h>
+#    include <private/qsslsocket_p.h>
 #    include <QtNetwork/qsslsocket.h>
 #    include <QtNetwork/qsslerror.h>
 #else
@@ -114,6 +117,7 @@ public:
     void setSslConfiguration(const QSslConfiguration &config);
     void ignoreSslErrors(int channel = -1);
     void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
+    QSharedPointer<QSslContext> sslContext();
 #endif
 
 private:
@@ -206,6 +210,10 @@ public:
     QList<HttpMessagePair> highPriorityQueue;
     QList<HttpMessagePair> lowPriorityQueue;
 
+#ifndef QT_NO_OPENSSL
+    QSharedPointer<QSslContext> sslContext;
+#endif
+
     friend class QHttpNetworkConnectionChannel;
 };
 
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 02daa50..fa18d64 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -938,6 +938,17 @@ void QHttpNetworkConnectionChannel::_q_connected()
         else
             close();
     }
+#ifndef QT_NO_OPENSSL
+    else if (pendingEncrypt) {
+        // Set the QSslContext from the connection to this socket. This will enable SSL session resuming.
+//        qDebug() << (void*)this
+//                 << "QHttpNetworkConnectionChannel::_q_connected()"
+//                 << socket->localPort();
+        QSslSocketPrivate::setSslContext(static_cast<QSslSocket*>(socket), connection->sslContext());
+
+        // FIXME copy the session here?
+    }
+#endif
 }
 
 
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index e4eb7c4..f2ffa4b 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -837,6 +837,12 @@ QSslConfiguration QHttpNetworkReply::sslConfiguration() const
 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
 {
     Q_D(QHttpNetworkReply);
+
+    // FIXME
+    // Setting a SSL configuration on a reply should not be supported. The user needs to set
+    // her/his QSslConfiguration on the QNetworkRequest.
+    qDebug() << "QHttpNetworkReply::setSslConfiguration";
+
     if (d->connection)
         d->connection->setSslConfiguration(config);
 }
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index 80b05a4..0bf585e 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -102,6 +102,8 @@ static QByteArray makeCacheKey(QNetworkAccessHttpBackend *backend, QNetworkProxy
     }
 #endif
 
+    // FIXME we need to take into account the SSL configuration!
+
     return "http-connection:" + result;
 }
 
@@ -570,8 +572,6 @@ void QNetworkAccessHttpBackend::postRequest()
     httpReply = http->sendRequest(httpRequest);
     httpReply->setParent(this);
 #ifndef QT_NO_OPENSSL
-    if (pendingSslConfiguration)
-        httpReply->setSslConfiguration(*pendingSslConfiguration);
     if (pendingIgnoreAllSslErrors)
         httpReply->ignoreSslErrors();
     httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList);
@@ -657,6 +657,13 @@ void QNetworkAccessHttpBackend::open()
         // the http object is actually a QHttpNetworkConnection
         http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt);
 
+#ifndef QT_NO_OPENSSL
+        // Set the QSslConfiguration from this QNetworkRequest.
+        if (encrypt) {
+            http->setSslConfiguration(request().sslConfiguration());
+        }
+#endif
+
 #ifndef QT_NO_NETWORKPROXY
         http->setTransparentProxy(transparentProxy);
         http->setCacheProxy(cacheProxy);
@@ -944,12 +951,8 @@ void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config)
 
 void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig)
 {
-    if (httpReply)
-        httpReply->setSslConfiguration(newconfig);
-    else if (pendingSslConfiguration)
-        *pendingSslConfiguration = newconfig;
-    else
-        pendingSslConfiguration = new QSslConfiguration(newconfig);
+    // Setting a SSL configuration on a reply is not supported. The user needs to set
+    // her/his QSslConfiguration on the QNetworkRequest.
 }
 #endif
 
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index d104d1c..346db50 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -124,6 +124,7 @@ public:
 private:
     friend class QSslSocket;
     friend class QSslConfigurationPrivate;
+    friend class QSslSocketBackendPrivate;
     QSslConfiguration(QSslConfigurationPrivate *dd);
     QSharedDataPointer<QSslConfigurationPrivate> d;
 };
diff --git a/src/network/ssl/qsslcontext.cpp b/src/network/ssl/qsslcontext.cpp
new file mode 100644
index 0000000..2431180
--- /dev/null
+++ b/src/network/ssl/qsslcontext.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsslcontext_p.h"
+#include "qsslsocket.h"
+#include "private/qsslsocket_p.h"
+
+#include "qmutex.h"
+
+#include "private/qsslsocket_openssl_symbols_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// qsslsocket_openssl.cpp
+extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
+extern QString getErrorsFromOpenSsl();
+
+QSslContext::QSslContext()
+    : ctx(0),
+    pkey(0),
+    session(0)
+{
+
+}
+
+
+QSslContext::~QSslContext()
+{
+    if (ctx) {
+        // This will decrement the reference count by 1 and free the context eventually when possible
+        q_SSL_CTX_free(ctx);
+    }
+
+    if (pkey) {
+        q_EVP_PKEY_free(pkey);
+    }
+
+    if (session)
+        q_SSL_SESSION_free(session);
+}
+
+QSslContext* QSslContext::fromConfiguration(bool clientMode, const QSslConfiguration&c)
+{
+    QSslContext *sslContext = new QSslContext();
+    sslContext->sslConfiguration = c;
+
+    // FIXME
+
+    bool reinitialized = false;
+init_context:
+    switch (sslContext->sslConfiguration.protocol()) {
+    case QSsl::SslV2:
+        sslContext->ctx = q_SSL_CTX_new(clientMode ? q_SSLv2_client_method() : q_SSLv2_server_method());
+        break;
+    case QSsl::SslV3:
+        sslContext->ctx = q_SSL_CTX_new(clientMode ? q_SSLv3_client_method() : q_SSLv3_server_method());
+        break;
+    case QSsl::AnyProtocol:
+    default:
+        sslContext->ctx = q_SSL_CTX_new(clientMode ? q_SSLv23_client_method() : q_SSLv23_server_method());
+        break;
+    case QSsl::TlsV1:
+        sslContext->ctx = q_SSL_CTX_new(clientMode ? q_TLSv1_client_method() : q_TLSv1_server_method());
+        break;
+    }
+    if (!sslContext->ctx) {
+        // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them
+        // by re-initializing the library.
+        if (!reinitialized) {
+            reinitialized = true;
+            if (q_SSL_library_init() == 1)
+                goto init_context;
+        }
+
+        // ### Bad error code
+        sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(getErrorsFromOpenSsl());
+        return sslContext;
+    }
+
+    // Enable all bug workarounds.
+    q_SSL_CTX_set_options(sslContext->ctx, SSL_OP_ALL);
+
+    // Initialize ciphers
+    QByteArray cipherString; // FIXME reserve some bytes
+    int first = true;
+    QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
+    if (ciphers.isEmpty())
+        ciphers = QSslSocketPrivate::defaultCiphers();
+    foreach (const QSslCipher &cipher, ciphers) {
+        if (first)
+            first = false;
+        else
+            cipherString.append(':');
+        cipherString.append(cipher.name().toLatin1());
+    }
+
+    if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, cipherString.data())) {
+        // ### Bad error code
+        sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(getErrorsFromOpenSsl());
+        return sslContext;
+    }
+
+    // Add all our CAs to this store.
+    QList<QSslCertificate> expiredCerts;
+    //foreach (const QSslCertificate &caCertificate, q->caCertificates()) {
+    foreach (const QSslCertificate &caCertificate, sslContext->sslConfiguration.caCertificates()) {
+
+        // add expired certs later, so that the
+        // valid ones are used before the expired ones
+        if (! caCertificate.isValid()) {
+            expiredCerts.append(caCertificate);
+        } else {
+            q_X509_STORE_add_cert(sslContext->ctx->cert_store, (X509 *)caCertificate.handle());
+        }
+    }
+    // now add the expired certs
+    foreach (const QSslCertificate &caCertificate, expiredCerts) {
+        q_X509_STORE_add_cert(sslContext->ctx->cert_store, (X509 *)caCertificate.handle());
+    }
+
+    // Register a custom callback to get all verification errors.
+    X509_STORE_set_verify_cb_func(sslContext->ctx->cert_store, q_X509Callback);
+
+    if (!sslContext->sslConfiguration.localCertificate().isNull()) {
+        // Require a private key as well.
+        if (sslContext->sslConfiguration.privateKey().isNull()) {
+            sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(getErrorsFromOpenSsl());
+            return sslContext;
+        }
+
+        // Load certificate
+        if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
+            sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(getErrorsFromOpenSsl());
+            return sslContext;
+        }
+
+        // Load private key
+        sslContext->pkey = q_EVP_PKEY_new();
+        // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
+        // this lead to a memory leak. Now we use the *_set1_* functions which do not
+        // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
+        if (sslContext->sslConfiguration.privateKey().algorithm() == QSsl::Rsa)
+            q_EVP_PKEY_set1_RSA(sslContext->pkey, (RSA *)sslContext->sslConfiguration.privateKey().handle());
+        else
+            q_EVP_PKEY_set1_DSA(sslContext->pkey, (DSA *)sslContext->sslConfiguration.privateKey().handle());
+        if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, sslContext->pkey)) {
+            sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl());
+            return sslContext;
+        }
+
+        // Check if the certificate matches the private key.
+        if (!q_SSL_CTX_check_private_key(sslContext->ctx)) {
+            sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(getErrorsFromOpenSsl());
+            return sslContext;
+        }
+    }
+
+    // Initialize peer verification.
+    if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
+        q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, 0);
+    } else {
+        q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER, q_X509Callback);
+    }
+
+    // Set verification depth.
+    if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
+        q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
+
+    return sslContext;
+}
+
+// Needs to be deleted by caller
+SSL* QSslContext::createSsl()
+{
+ //   return q_SSL_new(ctx);
+    SSL* ssl = q_SSL_new(ctx);
+    q_SSL_clear(ssl);
+
+    if (session) {
+        // Try to resume the last session we cached
+        int r = q_SSL_set_session(ssl, session);
+        Q_UNUSED(r);
+    }
+
+    return ssl;
+}
+
+// We cache exactly one session here
+void QSslContext::cacheSession(SSL* ssl)
+{
+    // dont cache the same session again
+//    if (session && 0 == q_SSL_SESSION_cmp(session, q_SSL_get_session(ssl)))
+//        return;
+    // FIXME
+
+     // decrease refcount of currently stored session
+    if (session)
+        q_SSL_SESSION_free(session);
+
+    // cache the session the caller gave us and increase reference count
+    session = q_SSL_get1_session(ssl);
+
+}
+
+QSslContext::SslContextError QSslContext::error() const
+{
+    return (errorStr.isNull() ? QSslContext::NoError : QSslContext::UnspecifiedError);
+}
+
+/*!
+    Returns a short localized human-readable description of the error.
+
+    \sa error(), certificate()
+*/
+QString QSslContext::errorString() const
+{
+    return errorStr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h
new file mode 100644
index 0000000..34c5a9d
--- /dev/null
+++ b/src/network/ssl/qsslcontext_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLCONTEXT_H
+#define QSSLCONTEXT_H
+
+#include <QtCore/qvariant.h>
+#include <QtNetwork/qsslcertificate.h>
+#include <QSslConfiguration>
+
+// FIXME Copied from qsslsocket_openssl.h.. All needed?
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#if defined(OCSP_RESPONSE)
+#undef OCSP_RESPONSE
+#endif
+#endif
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+// End Copied
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+// This is an internal Qt class used to enable SSL session sharing between multiple QSslSockets
+class QSslContextPrivate;
+class Q_NETWORK_EXPORT QSslContext
+{
+public:
+    enum SslContextError {
+        NoError,
+        UnspecifiedError = -1
+    };
+
+    ~QSslContext();
+
+    static QSslContext* fromConfiguration(bool clientMode, const QSslConfiguration&);
+
+    SslContextError error() const;
+    QString errorString() const;
+
+    //QSslCertificate certificate() const;
+
+    SSL* createSsl();
+    void cacheSession(SSL*); // should be called when handshake completed
+
+protected:
+    QSslContext();
+
+ //   int x509Callback(int ok, X509_STORE_CTX *ctx);
+  //  QList<QPair<int, int> > errorList;
+    friend class QSslBackendPrivate;
+private:
+    SSL_CTX* ctx;
+    EVP_PKEY *pkey;
+    SSL_SESSION *session;
+    QString errorStr;
+    QSslConfiguration sslConfiguration;
+};
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSSLCONTEXT_H
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index a752720..8ff98df 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -2192,6 +2192,12 @@ void QSslSocketPrivate::_q_flushReadBuffer()
         transmit();
 }
 
+void QSslSocketPrivate::setSslContext(QSslSocket* socket, QSharedPointer<QSslContext> c)
+{
+    if (socket->d_func()->sslContext.isNull())
+        socket->d_func()->sslContext = c;
+}
+
 QT_END_NAMESPACE
 
 // For private slots
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 0479d83..17c3dfc 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -78,6 +78,29 @@ QT_BEGIN_NAMESPACE
 #include <QtCore/private/qcore_symbian_p.h>
 #endif
 
+// later in this file.
+QString getErrorsFromOpenSsl();
+
+// ### This list is shared between all threads, and protected by a
+// mutex. Investigate using thread local storage instead.
+struct QSslErrorList
+{
+    QMutex mutex;
+    QList<QPair<int, int> > errors;
+};
+Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList)
+int q_X509Callback(int ok, X509_STORE_CTX *ctx)
+{
+    if (!ok) {
+        // Store the error and at which depth the error was detected.
+        _q_sslErrorList()->errors << qMakePair<int, int>(ctx->error, ctx->error_depth);
+    }
+    // Always return OK to allow verification to continue. We're handle the
+    // errors gracefully after collecting all errors, after verification has
+    // completed.
+    return 1;
+}
+
 bool QSslSocketPrivate::s_libraryLoaded = false;
 bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
 
@@ -164,8 +187,6 @@ static unsigned long id_function()
 
 QSslSocketBackendPrivate::QSslSocketBackendPrivate()
     : ssl(0),
-      ctx(0),
-      pkey(0),
       readBio(0),
       writeBio(0),
       session(0)
@@ -216,26 +237,6 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *ciph
     return ciph;
 }
 
-// ### This list is shared between all threads, and protected by a
-// mutex. Investigate using thread local storage instead.
-struct QSslErrorList
-{
-    QMutex mutex;
-    QList<QPair<int, int> > errors;
-};
-Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList)
-static int q_X509Callback(int ok, X509_STORE_CTX *ctx)
-{
-    if (!ok) {
-        // Store the error and at which depth the error was detected.
-        _q_sslErrorList()->errors << qMakePair<int, int>(ctx->error, ctx->error_depth);
-    }
-    // Always return OK to allow verification to continue. We're handle the
-    // errors gracefully after collecting all errors, after verification has
-    // completed.
-    return 1;
-}
-
 bool QSslSocketBackendPrivate::initSslContext()
 {
     Q_Q(QSslSocket);
@@ -243,134 +244,21 @@ bool QSslSocketBackendPrivate::initSslContext()
     // Create and initialize SSL context. Accept SSLv2, SSLv3 and TLSv1.
     bool client = (mode == QSslSocket::SslClientMode);
 
-    bool reinitialized = false;
-init_context:
-    switch (configuration.protocol) {
-    case QSsl::SslV2:
-        ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
-        break;
-    case QSsl::SslV3:
-        ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method());
-        break;
-    case QSsl::AnyProtocol:
-    default:
-        ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method());
-        break;
-    case QSsl::TlsV1:
-        ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method());
-        break;
-    }
-    if (!ctx) {
-        // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them
-        // by re-initializing the library.
-        if (!reinitialized) {
-            reinitialized = true;
-            if (q_SSL_library_init() == 1)
-                goto init_context;
-        }
-
-        // ### Bad error code
-        q->setErrorString(QSslSocket::tr("Error creating SSL context (%1)").arg(getErrorsFromOpenSsl()));
-        q->setSocketError(QAbstractSocket::UnknownSocketError);
-        emit q->error(QAbstractSocket::UnknownSocketError);
-        return false;
-    }
-
-    // Enable all bug workarounds.
-    q_SSL_CTX_set_options(ctx, SSL_OP_ALL);
-
-    // Initialize ciphers
-    QByteArray cipherString;
-    int first = true;
-    QList<QSslCipher> ciphers = configuration.ciphers;
-    if (ciphers.isEmpty())
-        ciphers = defaultCiphers();
-    foreach (const QSslCipher &cipher, ciphers) {
-        if (first)
-            first = false;
-        else
-            cipherString.append(':');
-        cipherString.append(cipher.name().toLatin1());
-    }
+    // If no external context was set (e.g. bei QHttpNetworkConnection) we will create a default context
+    if (!sslContext)
+        sslContext = QSharedPointer<QSslContext>(QSslContext::fromConfiguration(client, QSslConfiguration(&configuration)));
 
-    if (!q_SSL_CTX_set_cipher_list(ctx, cipherString.data())) {
-        // ### Bad error code
-        q->setErrorString(QSslSocket::tr("Invalid or empty cipher list (%1)").arg(getErrorsFromOpenSsl()));
+    if (sslContext->error() != QSslContext::NoError) {
+        qDebug() << (sslContext->errorString());
+        q->setErrorString(sslContext->errorString());
         q->setSocketError(QAbstractSocket::UnknownSocketError);
         emit q->error(QAbstractSocket::UnknownSocketError);
+        sslContext.clear(); // Deleted the QSslContext
         return false;
     }
 
-    // Add all our CAs to this store.
-    QList<QSslCertificate> expiredCerts;
-    foreach (const QSslCertificate &caCertificate, q->caCertificates()) {
-        // add expired certs later, so that the
-        // valid ones are used before the expired ones
-        if (! caCertificate.isValid()) {
-            expiredCerts.append(caCertificate);
-        } else {
-            q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
-        }
-    }
-    // now add the expired certs
-    foreach (const QSslCertificate &caCertificate, expiredCerts) {
-        q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
-    }
-
-    // Register a custom callback to get all verification errors.
-    X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback);
-
-    if (!configuration.localCertificate.isNull()) {
-        // Require a private key as well.
-        if (configuration.privateKey.isNull()) {
-            q->setErrorString(QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(getErrorsFromOpenSsl()));
-            emit q->error(QAbstractSocket::UnknownSocketError);
-            return false;
-        }
-
-        // Load certificate
-        if (!q_SSL_CTX_use_certificate(ctx, (X509 *)configuration.localCertificate.handle())) {
-            q->setErrorString(QSslSocket::tr("Error loading local certificate, %1").arg(getErrorsFromOpenSsl()));
-            emit q->error(QAbstractSocket::UnknownSocketError);
-            return false;
-        }
-
-        // Load private key
-        pkey = q_EVP_PKEY_new();
-        // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
-        // this lead to a memory leak. Now we use the *_set1_* functions which do not
-        // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
-        if (configuration.privateKey.algorithm() == QSsl::Rsa)
-            q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle());
-        else
-            q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle());
-        if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) {
-            q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl()));
-            emit q->error(QAbstractSocket::UnknownSocketError);
-            return false;
-        }
-
-        // Check if the certificate matches the private key.
-        if (!q_SSL_CTX_check_private_key(ctx)) {
-            q->setErrorString(QSslSocket::tr("Private key does not certify public key, %1").arg(getErrorsFromOpenSsl()));
-            emit q->error(QAbstractSocket::UnknownSocketError);
-            return false;
-        }
-    }
-
-    // Initialize peer verification.
-    if (configuration.peerVerifyMode == QSslSocket::VerifyNone) {
-        q_SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
-    } else {
-        q_SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, q_X509Callback);
-    }
-
-    // Set verification depth.
-    if (configuration.peerVerifyDepth != 0)
-        q_SSL_CTX_set_verify_depth(ctx, configuration.peerVerifyDepth);
-
     // Create and initialize SSL session
-    if (!(ssl = q_SSL_new(ctx))) {
+    if (!(ssl = sslContext->createSsl())) {
         // ### Bad error code
         q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl()));
         q->setSocketError(QAbstractSocket::UnknownSocketError);
@@ -379,7 +267,6 @@ init_context:
     }
 
     // Clear the session.
-    q_SSL_clear(ssl);
     errorList.clear();
 
     // Initialize memory BIOs for encryption and decryption.
@@ -861,6 +748,7 @@ void QSslSocketBackendPrivate::startClientEncryption()
 {
     if (!initSslContext()) {
         // ### report error: internal OpenSSL failure
+        qDebug() << "QSslSocketBackendPrivate::startClientEncryption() initSslContext failed";
         return;
     }
 
@@ -1066,7 +954,7 @@ void QSslSocketBackendPrivate::transmit()
                 break;
             }
         } while (ssl && readBytes > 0);
-    } while (ssl && ctx && transmitting);
+    } while (ssl && transmitting);
 }
 
 static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
@@ -1270,6 +1158,14 @@ bool QSslSocketBackendPrivate::startHandshake()
     if (readBufferMaxSize)
         plainSocket->setReadBufferSize(1024);
 
+#ifdef QSSLSOCKET_DEBUG
+    if (q_SSL_session_reused(ssl))
+        qDebug() << "QSslSocketBackendPrivate::startHandshake: We were able to re-use the previous SSL session";
+#endif
+
+    // Cache this SSL session inside the QSslContext
+    sslContext->cacheSession(ssl);
+
     connectionEncrypted = true;
     emit q->encrypted();
     if (autoStartHandshake && pendingClose) {
@@ -1294,20 +1190,13 @@ void QSslSocketBackendPrivate::disconnected()
         q_SSL_free(ssl);
         ssl = 0;
     }
-    if (ctx) {
-        q_SSL_CTX_free(ctx);
-        ctx = 0;
-    }
-    if (pkey) {
-        q_EVP_PKEY_free(pkey);
-        pkey = 0;
-    }
 
+    sslContext.clear();
 }
 
 QSslCipher QSslSocketBackendPrivate::sessionCipher() const
 {
-    if (!ssl || !ctx)
+    if (!ssl)
         return QSslCipher();
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
     // FIXME This is fairly evil, but needed to keep source level compatibility
@@ -1331,7 +1220,7 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
     return certificates;
 }
 
-QString QSslSocketBackendPrivate::getErrorsFromOpenSsl()
+QString getErrorsFromOpenSsl()
 {
     QString errorString;
     unsigned long errNum;
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 878c654..7a8b7b2 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -96,8 +96,8 @@ public:
     // SSL context
     bool initSslContext();
     SSL *ssl;
-    SSL_CTX *ctx;
-    EVP_PKEY *pkey;
+//    SSL_CTX *ctx;
+//    EVP_PKEY *pkey;
     BIO *readBio;
     BIO *writeBio;
     SSL_SESSION *session;
@@ -117,7 +117,6 @@ public:
     static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher);
     static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
     Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
-    static QString getErrorsFromOpenSsl();
 };
 
 #if defined(Q_OS_SYMBIAN)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index d717214..bcfe0c2 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -162,6 +162,7 @@ DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return)
 DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
 DEFINEFUNC3(char *, SSL_CIPHER_description, SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return)
 DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
+DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int b, b, long c, c, void *d, d, return -1, return)
 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
 // 0.9.8 broke SC and BC by changing this function's signature.
 DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
@@ -213,6 +214,17 @@ DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG
 DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG)
 DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG)
 DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return)
+
+//int q_SSL_set_session(SSL *to, SSL_SESSION *session);
+DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, return -1, return)
+
+//void q_SSL_SESSION_free(SSL_SESSION *ses);
+DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG)
+
+//SSL_SESSION *q_SSL_get1_session(const SSL *ssl);
+DEFINEFUNC(SSL_SESSION*, SSL_get1_session, const SSL *ssl, ssl, return 0, return)
+DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return 0, return)
+
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
 DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
 DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return)
@@ -575,6 +587,7 @@ bool q_resolveOpenSslSymbols()
     RESOLVEFUNC(SSL_accept, 82, libs.first )
     RESOLVEFUNC(SSL_clear, 92, libs.first )
     RESOLVEFUNC(SSL_connect, 93, libs.first )
+    RESOLVEFUNC(SSL_ctrl, 95, libs.first )
     RESOLVEFUNC(SSL_free, 99, libs.first )
     RESOLVEFUNC(SSL_get_ciphers, 104, libs.first )
     RESOLVEFUNC(SSL_get_current_cipher, 106, libs.first )
@@ -590,6 +603,10 @@ bool q_resolveOpenSslSymbols()
     RESOLVEFUNC(SSL_set_bio, 149, libs.first )
     RESOLVEFUNC(SSL_set_connect_state, 152, libs.first )
     RESOLVEFUNC(SSL_shutdown, 173, libs.first )
+    RESOLVEFUNC(SSL_set_session, FIXME, libs.first )
+    RESOLVEFUNC(SSL_SESSION_free, FIXME, libs.first )
+    RESOLVEFUNC(SSL_get1_session, FIXME, libs.first )
+    RESOLVEFUNC(SSL_get_session, FIXME, libs.first )
     RESOLVEFUNC(SSL_write, 188, libs.first )
     RESOLVEFUNC(SSLv2_client_method, 192, libs.first )
     RESOLVEFUNC(SSLv3_client_method, 195, libs.first )
@@ -699,6 +716,7 @@ bool q_resolveOpenSslSymbols()
     RESOLVEFUNC(SSL_accept)
     RESOLVEFUNC(SSL_clear)
     RESOLVEFUNC(SSL_connect)
+    RESOLVEFUNC(SSL_ctrl)
     RESOLVEFUNC(SSL_free)
     RESOLVEFUNC(SSL_get_ciphers)
     RESOLVEFUNC(SSL_get_current_cipher)
@@ -714,6 +732,10 @@ bool q_resolveOpenSslSymbols()
     RESOLVEFUNC(SSL_set_bio)
     RESOLVEFUNC(SSL_set_connect_state)
     RESOLVEFUNC(SSL_shutdown)
+    RESOLVEFUNC(SSL_set_session)
+    RESOLVEFUNC(SSL_SESSION_free)
+    RESOLVEFUNC(SSL_get1_session)
+    RESOLVEFUNC(SSL_get_session)
     RESOLVEFUNC(SSL_write)
     RESOLVEFUNC(SSLv2_client_method)
     RESOLVEFUNC(SSLv3_client_method)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index ef61dbf..f149a76 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -270,6 +270,7 @@ int q_SSL_accept(SSL *a);
 int q_SSL_clear(SSL *a);
 char *q_SSL_CIPHER_description(SSL_CIPHER *a, char *b, int c);
 int q_SSL_connect(SSL *a);
+long q_SSL_ctrl(SSL *a, int b, long c, void *d);
 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
 // 0.9.8 broke SC and BC by changing this function's signature.
 int q_SSL_CTX_check_private_key(const SSL_CTX *a);
@@ -321,6 +322,13 @@ void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
 void q_SSL_set_accept_state(SSL *a);
 void q_SSL_set_connect_state(SSL *a);
 int q_SSL_shutdown(SSL *a);
+
+int q_SSL_set_session(SSL *to, SSL_SESSION *session);
+void q_SSL_SESSION_free(SSL_SESSION *ses);
+SSL_SESSION *q_SSL_get1_session(const SSL *ssl);
+SSL_SESSION *q_SSL_get_session(const SSL *ssl);
+
+
 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
 const SSL_METHOD *q_SSLv2_client_method();
 const SSL_METHOD *q_SSLv3_client_method();
@@ -410,6 +418,20 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
 #else
 #define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_noconf()
 #endif
+#define q_SSL_CTX_set_session_cache_mode(ctx,m) \
+     q_SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL)
+#define q_SSL_CTX_sess_hits(ctx) \
+        q_SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_HIT,0,NULL)
+#define q_SSL_CTX_sess_misses(ctx) \
+        q_SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_MISSES,0,NULL)
+#define q_SSL_CTX_sess_timeouts(ctx) \
+        q_SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_TIMEOUTS,0,NULL)
+#define q_SSL_session_reused(ssl) \
+        q_SSL_ctrl((ssl),SSL_CTRL_GET_SESSION_REUSED,0,NULL)
+#define q_SSL_CTX_sess_number(ctx) \
+        q_SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_NUMBER,0,NULL)
+
+
 void q_OPENSSL_add_all_algorithms_noconf();
 void q_OPENSSL_add_all_algorithms_conf();
 
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 94f5f39..a08f9e0 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -59,6 +59,7 @@
 #include <private/qtcpsocket_p.h>
 #include "qsslkey.h"
 #include "qsslconfiguration_p.h"
+#include "qsslcontext_p.h"
 
 #include <QtCore/qstringlist.h>
 
@@ -105,7 +106,11 @@ public:
     QList<QSslError> ignoreErrorsList;
     bool* readyReadEmittedPointer;
 
+    static void setSslContext(QSslSocket*, QSharedPointer<QSslContext>);
+    QSharedPointer<QSslContext> sslContext;
     QSslConfigurationPrivate configuration;
+
+
     QList<QSslError> sslErrors;
 
     // if set, this hostname is used for certificate validation instead of the hostname
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index 8b2e2c1..599fe74 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -13,6 +13,7 @@ symbian {
                ssl/qsslcertificate_p.h \
 	       ssl/qsslconfiguration.h \
 	       ssl/qsslconfiguration_p.h \
+               ssl/qsslcontext_p.h \
                ssl/qsslcipher.h \
                ssl/qsslcipher_p.h \
                ssl/qsslerror.h \
@@ -24,6 +25,7 @@ symbian {
     SOURCES += ssl/qssl.cpp \
                ssl/qsslcertificate.cpp \
 	       ssl/qsslconfiguration.cpp \
+               ssl/qsslcontext.cpp \
                ssl/qsslcipher.cpp \
                ssl/qsslerror.cpp \
                ssl/qsslkey.cpp \
-- 
1.7.4.1

