Bug 674147 (Remove the SSL Thread) Part 2: Everything else, r=honzab
authorBrian Smith <bsmith@mozilla.com>
Thu, 01 Dec 2011 14:37:57 -0800
changeset 81146 8576199c846c2c9b08a3c1156c564dc48506b280
parent 81145 552810b7513bbe577daa857624ac4b7a2d449ca7
child 81147 7fe6db51869d248d5b3aa4a2c1a62fd62d10648e
push id3753
push usermak77@bonardo.net
push dateFri, 02 Dec 2011 11:15:00 +0000
treeherdermozilla-inbound@93c850b9f00f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs674147
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 674147 (Remove the SSL Thread) Part 2: Everything else, r=honzab
netwerk/base/src/nsSocketTransportService2.cpp
security/manager/ssl/src/Makefile.in
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/SSLServerCertVerification.h
security/manager/ssl/src/nsNSSCallbacks.cpp
security/manager/ssl/src/nsNSSCallbacks.h
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/src/nsNSSComponent.h
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
security/manager/ssl/src/nsSSLThread.cpp
security/manager/ssl/src/nsSSLThread.h
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -49,16 +49,24 @@
 #include "plstr.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIOService.h"
 
 #include "mozilla/FunctionTimer.h"
 
+// XXX: There is no good header file to put these in. :(
+namespace mozilla { namespace psm {
+
+void InitializeSSLServerCertVerificationThreads();
+void StopSSLServerCertVerificationThreads();
+
+} } // namespace mozilla::psm
+
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gSocketTransportLog = nsnull;
 #endif
 
 nsSocketTransportService *gSocketTransportService = nsnull;
 PRThread                 *gSocketThread           = nsnull;
@@ -604,16 +612,18 @@ nsSocketTransportService::AfterProcessNe
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::Run()
 {
     SOCKET_LOG(("STS thread init\n"));
 
+    psm::InitializeSSLServerCertVerificationThreads();
+
     gSocketThread = PR_GetCurrentThread();
 
     // add thread event to poll list (mThreadEvent may be NULL)
     mPollList[0].fd = mThreadEvent;
     mPollList[0].in_flags = PR_POLL_READ;
     mPollList[0].out_flags = 0;
 
     nsIThread *thread = NS_GetCurrentThread();
@@ -660,16 +670,18 @@ nsSocketTransportService::Run()
         DetachSocket(mIdleList, &mIdleList[i]);
 
     // Final pass over the event queue. This makes sure that events posted by
     // socket detach handlers get processed.
     NS_ProcessPendingEvents(thread);
 
     gSocketThread = nsnull;
 
+    psm::StopSSLServerCertVerificationThreads();
+
     SOCKET_LOG(("STS thread exit\n"));
     return NS_OK;
 }
 
 nsresult
 nsSocketTransportService::DoPollIteration(bool wait)
 {
     SOCKET_LOG(("STS poll iter [%d]\n", wait));
--- a/security/manager/ssl/src/Makefile.in
+++ b/security/manager/ssl/src/Makefile.in
@@ -55,17 +55,16 @@ LIBXUL_LIBRARY	= 1
 
 CPPSRCS = 				\
 	nsCERTValInParamWrapper.cpp     \
 	nsNSSCleaner.cpp                \
 	nsCertOverrideService.cpp   \
 	nsRecentBadCerts.cpp \
         nsClientAuthRemember.cpp        \
 	nsPSMBackgroundThread.cpp       \
-	nsSSLThread.cpp                 \
 	nsCertVerificationThread.cpp    \
 	nsProtectedAuthThread.cpp \
 	nsNSSCallbacks.cpp		\
 	nsNSSComponent.cpp		\
 	nsNSSErrors.cpp			\
 	nsNSSIOLayer.cpp		\
 	SSLServerCertVerification.cpp   \
 	nsSSLStatus.cpp		\
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -35,86 +35,254 @@
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
+
+/* 
+ * All I/O is done on the socket transport thread, including all calls into
+ * libssl. That is, all SSL_* functions must be called on the socket transport
+ * thread. This also means that all SSL callback functions will be called on
+ * the socket transport thread, including in particular the auth certificate
+ * hook.
+ *
+ * During certificate authentication, we call CERT_PKIXVerifyCert or
+ * CERT_VerifyCert. These functions may make zero or more HTTP requests
+ * for OCSP responses, CRLs, intermediate certificates, etc.
+ *
+ * If our cert auth hook were to call the CERT_*Verify* functions directly,
+ * there would be a deadlock: The CERT_*Verify* function would cause an event
+ * to be asynchronously posted to the socket transport thread, and then it
+ * would block the socket transport thread waiting to be notified of the HTTP
+ * response. However, the HTTP request would never actually be processed
+ * because the socket transport thread would be blocked and so it wouldn't be
+ * able process HTTP requests. (i.e. Deadlock.)
+ *
+ * Consequently, we must always call the CERT_*Verify* cert functions off the
+ * socket transport thread. To accomplish this, our auth cert hook dispatches a
+ * SSLServerCertVerificationJob to a pool of background threads, and then
+ * immediatley return SECWouldBlock to libssl. These jobs are where the
+ * CERT_*Verify* functions are actually called. 
+ *
+ * When our auth cert hook returns SECWouldBlock, libssl will carry on the
+ * handshake while we validate the certificate. This will free up the socket
+ * transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
+ * requests needed for cert verification as mentioned above--can be processed.
+ *
+ * Once the CERT_*Verify* function returns, the cert verification job
+ * dispatches a SSLServerCertVerificationResult to the socket transport thread;
+ * the SSLServerCertVerificationResult will notify libssl that the certificate
+ * authentication is complete. Once libssl is notified that the authentication
+ * is complete, it will continue the SSL handshake (if it hasn't already
+ * finished) and it will begin allowing us to send/receive data on the
+ * connection.
+ *
+ * Timeline of events:
+ *
+ *    * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
+ *      transport thread.
+ *    * SSLServerCertVerificationJob::Dispatch queues a job
+ *      (instance of SSLServerCertVerificationJob) to its background thread
+ *      pool and returns.
+ *    * One of the background threads calls CERT_*Verify*, which may enqueue
+ *      some HTTP request(s) onto the socket transport thread, and then
+ *      blocks that background thread waiting for the responses and/or timeouts
+ *      or errors for those requests.
+ *    * Once those HTTP responses have all come back or failed, the
+ *      CERT_*Verify* function returns a result indicating that the validation
+ *      succeeded or failed.
+ *    * If the validation succeeded, then a SSLServerCertVerificationResult
+ *      event is posted to the socket transport thread, and the cert
+ *      verification thread becomes free to verify other certificates.
+ *    * Otherwise, a CertErrorRunnable is posted to the socket transport thread
+ *      and then to the main thread (blocking both, see CertErrorRunnable) to
+ *      do cert override processing and bad cert listener notification. Then
+ *      the cert verification thread becomes free to verify other certificates.
+ *    * After processing cert overrides, the CertErrorRunnable will dispatch a
+ *      SSLServerCertVerificationResult event to the socket transport thread to
+ *      notify it of the result of the override processing; then it returns,
+ *      freeing up the main thread.
+ *    * The SSLServerCertVerificationResult event will either wake up the 
+ *      socket (using SSL_RestartHandshakeAfterServerCert) if validation
+ *      succeeded or there was an error override, or it will set an error flag
+ *      so that the next I/O operation on the socket will fail, causing the
+ *      socket transport thread to close the connection.
+ *
+ * Cert override processing must happen on the main thread because it accesses
+ * the nsICertOverrideService, and that service must be accessed on the main 
+ * thread because some extensions (Selenium, in particular) replace it with a
+ * Javascript implementation, and chrome JS must always be run on the main
+ * thread.
+ *
+ * SSLServerCertVerificationResult must be dispatched to the socket transport
+ * thread because we must only call SSL_* functions on the socket transport
+ * thread since they may do I/O, because many parts of nsNSSSocketInfo and
+ * the PSM NSS I/O layer are not thread-safe, and because we need the event to
+ * interrupt the PR_Poll that may waiting for I/O on the socket for which we
+ * are validating the cert.
+ */
+
+#include "SSLServerCertVerification.h"
 #include "nsNSSComponent.h"
 #include "nsNSSCertificate.h"
-#include "nsNSSCleaner.h"
 #include "nsNSSIOLayer.h"
 
+#include "nsIThreadPool.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
 #include "ssl.h"
-#include "cert.h"
 #include "secerr.h"
 #include "sslerr.h"
 
-using namespace mozilla;
-
-static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
-
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
 
+namespace mozilla { namespace psm {
+
+namespace {
+// do not use a nsCOMPtr to avoid static initializer/destructor
+nsIThreadPool * gCertVerificationThreadPool = nsnull;
+} // unnamed namespace
+
+// Called when the socket transport thread starts, to initialize the SSL cert
+// verification thread pool. By tying the thread pool startup/shutdown directly
+// to the STS thread's lifetime, we ensure that they are *always* available for
+// SSL connections and that there are no races during startup and especially
+// shutdown. (Previously, we have had multiple problems with races in PSM
+// background threads, and the race-prevention/shutdown logic used there is
+// brittle. Since this service is critical to things like downloading updates,
+// we take no chances.) Also, by doing things this way, we avoid the need for
+// locks, since gCertVerificationThreadPool is only ever accessed on the socket
+// transport thread.
+void
+InitializeSSLServerCertVerificationThreads()
+{
+  // TODO: tuning, make parameters preferences
+  // XXX: instantiate nsThreadPool directly, to make this more bulletproof.
+  // Currently, the nsThreadPool.h header isn't exported for us to do so.
+  nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
+                                   &gCertVerificationThreadPool);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to create SSL cert verification threads.");
+    return;
+  }
+
+  (void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
+  (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
+  (void) gCertVerificationThreadPool->SetThreadLimit(5);
+}
+
+// Called when the socket transport thread finishes, to destroy the thread
+// pool. Since the socket transport service has stopped processing events, it
+// will not attempt any more SSL I/O operations, so it is clearly safe to shut
+// down the SSL cert verification infrastructure. Also, the STS will not
+// dispatch many SSL verification result events at this point, so any pending
+// cert verifications will (correctly) fail at the point they are dispatched.
+//
+// The other shutdown race condition that is possible is a race condition with
+// shutdown of the nsNSSComponent service. We use the
+// nsNSSShutdownPreventionLock where needed (not here) to prevent that.
+void StopSSLServerCertVerificationThreads()
+{
+  if (gCertVerificationThreadPool) {
+    gCertVerificationThreadPool->Shutdown();
+    NS_RELEASE(gCertVerificationThreadPool);
+  }
+}
+
+namespace {
+
+class SSLServerCertVerificationJob : public nsRunnable
+{
+public:
+  // Must be called only on the socket transport thread
+  static SECStatus Dispatch(const void * fdForLogging,
+                            nsNSSSocketInfo * infoObject,
+                            CERTCertificate * serverCert);
+private:
+  NS_DECL_NSIRUNNABLE
+
+  // Must be called only on the socket transport thread
+  SSLServerCertVerificationJob(const void * fdForLogging,
+                               nsNSSSocketInfo & socketInfo, 
+                               CERTCertificate & cert);
+  ~SSLServerCertVerificationJob();
+
+  // Runs on one of the background threads
+  SECStatus AuthCertificate(const nsNSSShutDownPreventionLock & proofOfLock);
+
+  const void * const mFdForLogging;
+  const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
+  CERTCertificate * const mCert;
+};
+
+SSLServerCertVerificationJob::SSLServerCertVerificationJob(
+    const void * fdForLogging, nsNSSSocketInfo & socketInfo,
+    CERTCertificate & cert)
+  : mFdForLogging(fdForLogging)
+  , mSocketInfo(&socketInfo)
+  , mCert(CERT_DupCertificate(&cert))
+{
+}
+
+SSLServerCertVerificationJob::~SSLServerCertVerificationJob()
+{
+  CERT_DestroyCertificate(mCert);
+}
+
+static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
 SECStatus
-PSM_SSL_PKIX_AuthCertificate(PRFileDesc *fd, CERTCertificate *peerCert, bool checksig, bool isServer)
+PSM_SSL_PKIX_AuthCertificate(CERTCertificate *peerCert, void * pinarg,
+                             const char * hostname,
+                             const nsNSSShutDownPreventionLock & /*proofOfLock*/)
 {
     SECStatus          rv;
-    SECCertUsage       certUsage;
-    SECCertificateUsage certificateusage;
-    void *             pinarg;
-    char *             hostname;
     
-    pinarg = SSL_RevealPinArg(fd);
-    hostname = SSL_RevealURL(fd);
-    
-    /* this may seem backwards, but isn't. */
-    certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;
-    certificateusage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
-
     if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
-        rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, checksig, certUsage,
-                                pinarg);
+        rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, true,
+                                certUsageSSLServer, pinarg);
     }
     else {
         nsresult nsrv;
         nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
         if (!inss)
           return SECFailure;
         nsRefPtr<nsCERTValInParamWrapper> survivingParams;
         if (NS_FAILED(inss->GetDefaultCERTValInParam(survivingParams)))
           return SECFailure;
 
         CERTValOutParam cvout[1];
         cvout[0].type = cert_po_end;
 
-        rv = CERT_PKIXVerifyCert(peerCert, certificateusage,
+        rv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
                                 survivingParams->GetRawPointerForNSS(),
                                 cvout, pinarg);
     }
 
-    if ( rv == SECSuccess && !isServer ) {
+    if (rv == SECSuccess) {
         /* cert is OK.  This is the client side of an SSL connection.
         * Now check the name field in the cert against the desired hostname.
         * NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
         */
         if (hostname && hostname[0])
             rv = CERT_VerifyCertName(peerCert, hostname);
         else
             rv = SECFailure;
         if (rv != SECSuccess)
             PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
     }
         
-    PORT_Free(hostname);
     return rv;
 }
 
 struct nsSerialBinaryBlacklistEntry
 {
   unsigned int len;
   const char *binary_serial;
 };
@@ -192,32 +360,27 @@ PSM_SSL_BlacklistDigiNotar(CERTCertifica
     // let's see if we want to worsen the error code to revoked.
     PRErrorCode revoked_code = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, serverCertChain);
     return (revoked_code != 0) ? revoked_code : SEC_ERROR_UNTRUSTED_ISSUER;
   }
 
   return 0;
 }
 
-
-SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
-                                              PRBool checksig, PRBool isServer) {
-  nsNSSShutDownPreventionLock locker;
-
-  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
-  CERTCertificateCleaner serverCertCleaner(serverCert);
-
-  if (serverCert && 
-      serverCert->serialNumber.data &&
-      serverCert->issuerName &&
-      !strcmp(serverCert->issuerName, 
+SECStatus
+SSLServerCertVerificationJob::AuthCertificate(
+  nsNSSShutDownPreventionLock const & nssShutdownPreventionLock)
+{
+  if (mCert->serialNumber.data &&
+      mCert->issuerName &&
+      !strcmp(mCert->issuerName, 
         "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US")) {
 
-    unsigned char *server_cert_comparison_start = (unsigned char*)serverCert->serialNumber.data;
-    unsigned int server_cert_comparison_len = serverCert->serialNumber.len;
+    unsigned char *server_cert_comparison_start = mCert->serialNumber.data;
+    unsigned int server_cert_comparison_len = mCert->serialNumber.len;
 
     while (server_cert_comparison_len) {
       if (*server_cert_comparison_start != 0)
         break;
 
       ++server_cert_comparison_start;
       --server_cert_comparison_len;
     }
@@ -239,124 +402,281 @@ SECStatus PR_CALLBACK AuthCertificateCal
       if (server_cert_comparison_len == locked_cert_comparison_len &&
           !memcmp(server_cert_comparison_start, locked_cert_comparison_start, locked_cert_comparison_len)) {
         PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
         return SECFailure;
       }
     }
   }
 
-  SECStatus rv = PSM_SSL_PKIX_AuthCertificate(fd, serverCert, checksig, isServer);
+  SECStatus rv = PSM_SSL_PKIX_AuthCertificate(mCert, mSocketInfo,
+                                              mSocketInfo->GetHostName(),
+                                              nssShutdownPreventionLock);
 
   // We want to remember the CA certs in the temp db, so that the application can find the
   // complete chain at any time it might need it.
   // But we keep only those CA certs in the temp db, that we didn't already know.
 
-  if (serverCert) {
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-    nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
-    nsRefPtr<nsNSSCertificate> nsc;
+  nsRefPtr<nsSSLStatus> status = mSocketInfo->SSLStatus();
+  nsRefPtr<nsNSSCertificate> nsc;
 
-    if (!status || !status->mServerCert) {
-      nsc = nsNSSCertificate::Create(serverCert);
-    }
+  if (!status || !status->mServerCert) {
+    nsc = nsNSSCertificate::Create(mCert);
+  }
 
-    CERTCertList *certList = nsnull;
-    certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);
-    if (!certList) {
-      rv = SECFailure;
-    } else {
-      PRErrorCode blacklistErrorCode;
-      if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
-        blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(serverCert, certList);
-      } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
-        PRErrorCode savedErrorCode = PORT_GetError();
-        // Check if we want to worsen the error code to "revoked".
-        blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, certList);
-        if (blacklistErrorCode == 0) {
-          // we don't worsen the code, let's keep the original error code from NSS
-          PORT_SetError(savedErrorCode);
-        }
-      }
-      
-      if (blacklistErrorCode != 0) {
-        infoObject->SetCertIssuerBlacklisted();
-        PORT_SetError(blacklistErrorCode);
-        rv = SECFailure;
+  CERTCertList *certList = nsnull;
+  certList = CERT_GetCertChainFromCert(mCert, PR_Now(), certUsageSSLCA);
+  if (!certList) {
+    rv = SECFailure;
+  } else {
+    PRErrorCode blacklistErrorCode;
+    if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
+      blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(mCert, certList);
+    } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
+      PRErrorCode savedErrorCode = PORT_GetError();
+      // Check if we want to worsen the error code to "revoked".
+      blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(mCert, certList);
+      if (blacklistErrorCode == 0) {
+        // we don't worsen the code, let's keep the original error code from NSS
+        PORT_SetError(savedErrorCode);
       }
     }
-
-    if (rv == SECSuccess) {
-      if (nsc) {
-        bool dummyIsEV;
-        nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
-      }
-    
-      nsCOMPtr<nsINSSComponent> nssComponent;
       
-      for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
-           !CERT_LIST_END(node, certList);
-           node = CERT_LIST_NEXT(node)) {
-
-        if (node->cert->slot) {
-          // This cert was found on a token, no need to remember it in the temp db.
-          continue;
-        }
+    if (blacklistErrorCode != 0) {
+      mSocketInfo->SetCertIssuerBlacklisted();
+      PORT_SetError(blacklistErrorCode);
+      rv = SECFailure;
+    }
+  }
 
-        if (node->cert->isperm) {
-          // We don't need to remember certs already stored in perm db.
-          continue;
-        }
-        
-        if (node->cert == serverCert) {
-          // We don't want to remember the server cert, 
-          // the code that cares for displaying page info does this already.
-          continue;
-        }
+  if (rv == SECSuccess) {
+    if (nsc) {
+      bool dummyIsEV;
+      nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
+    }
+    
+    nsCOMPtr<nsINSSComponent> nssComponent;
+      
+    for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
+         !CERT_LIST_END(node, certList);
+         node = CERT_LIST_NEXT(node)) {
 
-        // We have found a signer cert that we want to remember.
-        char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
-        if (nickname && *nickname) {
-          PK11SlotInfo *slot = PK11_GetInternalKeySlot();
-          if (slot) {
-            PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
-                            nickname, false);
-            PK11_FreeSlot(slot);
-          }
-        }
-        PR_FREEIF(nickname);
+      if (node->cert->slot) {
+        // This cert was found on a token, no need to remember it in the temp db.
+        continue;
       }
 
+      if (node->cert->isperm) {
+        // We don't need to remember certs already stored in perm db.
+        continue;
+      }
+        
+      if (node->cert == mCert) {
+        // We don't want to remember the server cert, 
+        // the code that cares for displaying page info does this already.
+        continue;
+      }
+
+      // We have found a signer cert that we want to remember.
+      char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
+      if (nickname && *nickname) {
+        PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+        if (slot) {
+          PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
+                          nickname, false);
+          PK11_FreeSlot(slot);
+        }
+      }
+      PR_FREEIF(nickname);
     }
 
     if (certList) {
       CERT_DestroyCertList(certList);
     }
 
     // The connection may get terminated, for example, if the server requires
     // a client cert. Let's provide a minimal SSLStatus
     // to the caller that contains at least the cert and its status.
     if (!status) {
       status = new nsSSLStatus();
-      infoObject->SetSSLStatus(status);
+      mSocketInfo->SetSSLStatus(status);
     }
 
     if (rv == SECSuccess) {
       // Certificate verification succeeded delete any potential record
       // of certificate error bits.
       nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
-        infoObject, nsnull, rv);
+        mSocketInfo, nsnull, rv);
     }
     else {
       // Certificate verification failed, update the status' bits.
       nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
-        infoObject, status);
+        mSocketInfo, status);
     }
 
     if (status && !status->mServerCert) {
       status->mServerCert = nsc;
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-             ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get()));
+             ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
     }
   }
 
   return rv;
 }
+
+/*static*/ SECStatus
+SSLServerCertVerificationJob::Dispatch(const void * fdForLogging,
+                                       nsNSSSocketInfo * socketInfo,
+                                       CERTCertificate * serverCert)
+{
+  // Runs on the socket transport thread
+
+  if (!socketInfo || !serverCert) {
+    NS_ERROR("Invalid parameters for SSL server cert validation");
+    socketInfo->SetCertVerificationResult(PR_INVALID_STATE_ERROR,
+                                          PlainErrorMessage);
+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
+    return SECFailure;
+  }
+  
+  nsRefPtr<SSLServerCertVerificationJob> job
+    = new SSLServerCertVerificationJob(fdForLogging, *socketInfo, *serverCert);
+
+  socketInfo->SetCertVerificationWaiting();
+  nsresult nrv;
+  if (!gCertVerificationThreadPool) {
+    nrv = NS_ERROR_NOT_INITIALIZED;
+  } else {
+    nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
+  }
+  if (NS_FAILED(nrv)) {
+    PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
+                      ? SEC_ERROR_NO_MEMORY
+                      : PR_INVALID_STATE_ERROR;
+    socketInfo->SetCertVerificationResult(error, PlainErrorMessage);
+    PORT_SetError(error);
+    return SECFailure;
+  }
+
+  PORT_SetError(PR_WOULD_BLOCK_ERROR);
+  return SECWouldBlock;    
+}
+
+NS_IMETHODIMP
+SSLServerCertVerificationJob::Run()
+{
+  // Runs on a cert verification thread
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+          ("[%p] SSLServerCertVerificationJob::Run\n", mSocketInfo.get()));
+
+  PRErrorCode error;
+
+  nsNSSShutDownPreventionLock nssShutdownPrevention;
+  if (mSocketInfo->isAlreadyShutDown()) {
+    error = SEC_ERROR_USER_CANCELLED;
+  } else {
+    // Reset the error code here so we can detect if AuthCertificate fails to
+    // set the error code if/when it fails.
+    PR_SetError(0, 0); 
+    SECStatus rv = AuthCertificate(nssShutdownPrevention);
+    if (rv == SECSuccess) {
+      nsRefPtr<SSLServerCertVerificationResult> restart 
+        = new SSLServerCertVerificationResult(*mSocketInfo, 0);
+      restart->Dispatch();
+      return NS_OK;
+    }
+
+    error = PR_GetError();
+    if (error != 0) {
+      rv = HandleBadCertificate(error, mSocketInfo, *mCert, mFdForLogging,
+                                nssShutdownPrevention);
+      if (rv == SECSuccess) {
+        // The CertErrorRunnable will run on the main thread and it will dispatch
+        // the cert verification result to the socket transport thread, so we
+        // don't have to. This way, this verification thread doesn't need to
+        // wait for the CertErrorRunnable to complete.
+        return NS_OK; 
+      }
+      // DispatchCertErrorRunnable set a new error code.
+      error = PR_GetError(); 
+    }
+  }
+
+  if (error == 0) {
+    NS_NOTREACHED("no error set during certificate validation failure");
+    error = PR_INVALID_STATE_ERROR;
+  }
+
+  nsRefPtr<SSLServerCertVerificationResult> failure
+    = new SSLServerCertVerificationResult(*mSocketInfo, error);
+  failure->Dispatch();
+  return NS_OK;
+}
+
+} // unnamed namespace
+
+// Extracts whatever information we need out of fd (using SSL_*) and passes it
+// to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
+// never do anything with fd except logging.
+SECStatus
+AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
+{
+  // Runs on the socket transport thread
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+         ("[%p] starting AuthCertificateHook\n", fd));
+
+  // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
+  // doing verification without checking signatures.
+  NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
+
+  // PSM never causes libssl to call this function with PR_TRUE for isServer,
+  // and many things in PSM assume that we are a client.
+  NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
+
+  if (!checkSig || isServer) {
+      PR_SetError(PR_INVALID_STATE_ERROR, 0);
+      return SECFailure;
+  }
+      
+  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
+
+  nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
+  SECStatus rv = SSLServerCertVerificationJob::Dispatch(
+                        static_cast<const void *>(fd), socketInfo, serverCert);
+
+  CERT_DestroyCertificate(serverCert);
+
+  return rv;
+}
+
+SSLServerCertVerificationResult::SSLServerCertVerificationResult(
+        nsNSSSocketInfo & socketInfo, PRErrorCode errorCode,
+        SSLErrorMessageType errorMessageType)
+  : mSocketInfo(&socketInfo)
+  , mErrorCode(errorCode)
+  , mErrorMessageType(errorMessageType)
+{
+}
+
+void
+SSLServerCertVerificationResult::Dispatch()
+{
+  nsresult rv;
+  nsCOMPtr<nsIEventTarget> stsTarget
+    = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  NS_ASSERTION(stsTarget,
+               "Failed to get socket transport service event target");
+  rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+  NS_ASSERTION(NS_SUCCEEDED(rv), 
+               "Failed to dispatch SSLServerCertVerificationResult");
+}
+
+NS_IMETHODIMP
+SSLServerCertVerificationResult::Run()
+{
+  // TODO: Assert that we're on the socket transport thread
+  mSocketInfo->SetCertVerificationResult(mErrorCode, mErrorMessageType);
+  return NS_OK;
+}
+
+} } // namespace mozilla::psm
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/SSLServerCertVerification.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#ifndef _SSLSERVERCERTVERIFICATION_H
+#define _SSLSERVERCERTVERIFICATION_H
+
+#include "seccomon.h"
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+#include "nsIRunnable.h"
+#include "prerror.h"
+#include "nsNSSIOLayer.h"
+
+typedef struct PRFileDesc PRFileDesc;
+typedef struct CERTCertificateStr CERTCertificate;
+class nsNSSSocketInfo;
+class nsNSSShutDownPreventionLock;
+
+namespace mozilla { namespace psm {
+
+SECStatus AuthCertificateHook(void *arg, PRFileDesc *fd, 
+                              PRBool checkSig, PRBool isServer);
+
+SECStatus HandleBadCertificate(PRErrorCode defaultErrorCodeToReport,
+                               nsNSSSocketInfo * socketInfo,
+                               CERTCertificate & cert,
+                               const void * fdForLogging,
+                               const nsNSSShutDownPreventionLock &);
+
+// Dispatched from a cert verification thread to the STS thread to notify the
+// socketInfo of the verification result.
+//
+// This will cause the PR_Poll in the STS thread to return, so things work
+// correctly even if the STS thread is blocked polling (only) on the file
+// descriptor that is waiting for this result.
+class SSLServerCertVerificationResult : public nsRunnable
+{
+public:
+  NS_DECL_NSIRUNNABLE
+
+  SSLServerCertVerificationResult(nsNSSSocketInfo & socketInfo,
+                                  PRErrorCode errorCode,
+                                  SSLErrorMessageType errorMessageType = 
+                                      PlainErrorMessage);
+
+  void Dispatch();
+private:
+  const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
+  const PRErrorCode mErrorCode;
+  const SSLErrorMessageType mErrorMessageType;
+};
+
+} } // namespace mozilla::psm
+
+#endif
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -43,17 +43,16 @@
 #include "nsNSSComponent.h"
 #include "nsNSSCallbacks.h"
 #include "nsNSSIOLayer.h"
 #include "nsIWebProgressListener.h"
 #include "nsProtectedAuthThread.h"
 #include "nsITokenDialogs.h"
 #include "nsNSSShutDown.h"
 #include "nsIUploadChannel.h"
-#include "nsSSLThread.h"
 #include "nsThreadUtils.h"
 #include "nsIPrompt.h"
 #include "nsProxyRelease.h"
 #include "PSMRunnable.h"
 #include "nsIConsoleService.h"
 
 #include "ssl.h"
 #include "ocsp.h"
@@ -415,21 +414,20 @@ nsNSSHttpRequestSession::internal_send_r
 
       waitCondition.Wait(wait_interval);
       
       if (!waitFlag)
         break;
 
       if (!request_canceled)
       {
-        bool wantExit = nsSSLThread::stoppedOrStopping();
         bool timeout = 
           (PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval;
-
-        if (wantExit || timeout)
+ 
+        if (timeout)
         {
           request_canceled = true;
 
           nsRefPtr<nsCancelHTTPDownloadEvent> cancelevent = new nsCancelHTTPDownloadEvent;
           cancelevent->mListener = mListener;
           rv = NS_DispatchToMainThread(cancelevent);
           if (NS_FAILED(rv)) {
             NS_WARNING("cannot post cancel event");
@@ -816,16 +814,22 @@ void PR_CALLBACK HandshakeCallback(PRFil
   nsNSSShutDownPreventionLock locker;
   PRInt32 sslStatus;
   char* signer = nsnull;
   char* cipherName = nsnull;
   PRInt32 keyLength;
   nsresult rv;
   PRInt32 encryptBits;
 
+  nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
+
+  // If the handshake completed, then we know the site is TLS tolerant (if this
+  // was a TLS connection).
+  nsSSLIOLayerHelpers::rememberTolerantSite(fd, infoObject);
+
   if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength,
                                        &encryptBits, &signer, nsnull)) {
     return;
   }
 
   PRInt32 secStatus;
   if (sslStatus == SSL_SECURITY_STATUS_OFF)
     secStatus = nsIWebProgressListener::STATE_IS_BROKEN;
@@ -837,17 +841,16 @@ void PR_CALLBACK HandshakeCallback(PRFil
                  nsIWebProgressListener::STATE_SECURE_LOW);
 
   PRBool siteSupportsSafeRenego;
   if (SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, &siteSupportsSafeRenego) != SECSuccess
       || !siteSupportsSafeRenego) {
 
     bool wantWarning = (nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746() > 0);
 
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
     nsCOMPtr<nsIConsoleService> console;
     if (infoObject && wantWarning) {
       console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
       if (console) {
         nsXPIDLCString hostName;
         infoObject->GetHostName(getter_Copies(hostName));
 
         nsAutoString msg;
--- a/security/manager/ssl/src/nsNSSCallbacks.h
+++ b/security/manager/ssl/src/nsNSSCallbacks.h
@@ -47,21 +47,16 @@
 #include "nsIStreamLoader.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
 
 char* PR_CALLBACK
 PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg);
 
 void PR_CALLBACK HandshakeCallback(PRFileDesc *fd, void *client_data);
-SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
-                                              PRBool checksig, PRBool isServer);
-
-PRErrorCode PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
-                                       CERTCertList * serverCertChain);
 
 SECStatus RegisterMyOCSPAIAInfoCallback();
 SECStatus UnregisterMyOCSPAIAInfoCallback();
 
 class nsHTTPListener : public nsIStreamLoaderObserver
 {
 private:
   // For XPCOM implementations that are not a base class for some other
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -42,17 +42,16 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsNSSComponent.h"
 #include "nsNSSCallbacks.h"
 #include "nsNSSIOLayer.h"
-#include "nsSSLThread.h"
 #include "nsCertVerificationThread.h"
 
 #include "nsNetUtil.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryService.h"
 #include "nsIStreamListener.h"
 #include "nsIStringBundle.h"
 #include "nsIDirectoryService.h"
@@ -359,17 +358,17 @@ bool EnsureNSSInitialized(EnsureNSSOpera
   }
 }
 
 nsNSSComponent::nsNSSComponent()
   :mutex("nsNSSComponent.mutex"),
    mNSSInitialized(false),
    mCrlTimerLock("nsNSSComponent.mCrlTimerLock"),
    mThreadList(nsnull),
-   mSSLThread(NULL), mCertVerificationThread(NULL)
+   mCertVerificationThread(NULL)
 {
 #ifdef PR_LOGGING
   if (!gPIPNSSLog)
     gPIPNSSLog = PR_NewLogModule("pipnss");
 #endif
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::ctor\n"));
   mUpdateTimerInitialized = false;
   crlDownloadTimerOn = false;
@@ -386,47 +385,32 @@ nsNSSComponent::nsNSSComponent()
   hashTableCerts = nsnull;
   mShutdownObjectList = nsNSSShutDownList::construct();
   mIsNetworkDown = false;
 }
 
 void 
 nsNSSComponent::deleteBackgroundThreads()
 {
-  if (mSSLThread)
-  {
-    mSSLThread->requestExit();
-    delete mSSLThread;
-    mSSLThread = nsnull;
-  }
   if (mCertVerificationThread)
   {
     mCertVerificationThread->requestExit();
     delete mCertVerificationThread;
     mCertVerificationThread = nsnull;
   }
 }
 
 void
 nsNSSComponent::createBackgroundThreads()
 {
-  NS_ASSERTION(mSSLThread == nsnull, "SSL thread already created.");
   NS_ASSERTION(mCertVerificationThread == nsnull,
                "Cert verification thread already created.");
 
-  mSSLThread = new nsSSLThread;
-  nsresult rv = mSSLThread->startThread();
-  if (NS_FAILED(rv)) {
-    delete mSSLThread;
-    mSSLThread = nsnull;
-    return;
-  }
-
   mCertVerificationThread = new nsCertVerificationThread;
-  rv = mCertVerificationThread->startThread();
+  nsresult rv = mCertVerificationThread->startThread();
   if (NS_FAILED(rv)) {
     delete mCertVerificationThread;
     mCertVerificationThread = nsnull;
   }
 }
 
 nsNSSComponent::~nsNSSComponent()
 {
@@ -1994,17 +1978,17 @@ nsNSSComponent::Init()
   mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
   nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
   
   mClientAuthRememberService = new nsClientAuthRememberService;
   if (mClientAuthRememberService)
     mClientAuthRememberService->Init();
 
   createBackgroundThreads();
-  if (!mSSLThread || !mCertVerificationThread)
+  if (!mCertVerificationThread)
   {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, could not create threads\n"));
 
     DeregisterObservers();
     mPIPNSSBundle = nsnull;
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
@@ -2544,18 +2528,16 @@ nsNSSComponent::DoProfileApproveChange(n
       status->VetoChange();
     }
   }
 }
 
 void
 nsNSSComponent::DoProfileChangeNetTeardown()
 {
-  if (mSSLThread)
-    mSSLThread->requestExit();
   if (mCertVerificationThread)
     mCertVerificationThread->requestExit();
   mIsNetworkDown = true;
 }
 
 void
 nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
 {
--- a/security/manager/ssl/src/nsNSSComponent.h
+++ b/security/manager/ssl/src/nsNSSComponent.h
@@ -232,17 +232,16 @@ private:
   ~nsCryptoHMAC();
   PK11Context* mHMACContext;
 
   virtual void virtualDestroyNSSReference();
   void destructorSafeDestroyNSSReference();
 };
 
 class nsNSSShutDownList;
-class nsSSLThread;
 class nsCertVerificationThread;
 
 // Implementation of the PSM component interface.
 class nsNSSComponent : public nsISignatureVerifier,
                        public nsIEntropyCollector,
                        public nsINSSComponent,
                        public nsIObserver,
                        public nsSupportsWeakReference,
@@ -352,17 +351,16 @@ private:
   bool mUpdateTimerInitialized;
   static int mInstanceCount;
   nsNSSShutDownList *mShutdownObjectList;
   SmartCardThreadList *mThreadList;
   bool mIsNetworkDown;
 
   void deleteBackgroundThreads();
   void createBackgroundThreads();
-  nsSSLThread *mSSLThread;
   nsCertVerificationThread *mCertVerificationThread;
 
   nsNSSHttpInterface mHttpForNSS;
   nsRefPtr<nsClientAuthRememberService> mClientAuthRememberService;
   nsRefPtr<nsCERTValInParamWrapper> mDefaultCERTValInParam;
   nsRefPtr<nsCERTValInParamWrapper> mDefaultCERTValInParamLocalOnly;
 
   static PRStatus PR_CALLBACK IdentityInfoInit(void);
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -65,17 +65,17 @@
 #include "nsIStrictTransportSecurityService.h"
 
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsHashSets.h"
 #include "nsCRT.h"
 #include "nsAutoPtr.h"
 #include "nsPrintfCString.h"
-#include "nsSSLThread.h"
+#include "SSLServerCertVerification.h"
 #include "nsNSSShutDown.h"
 #include "nsSSLStatus.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCleaner.h"
 #include "nsThreadUtils.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsISecureBrowserUI.h"
@@ -105,18 +105,18 @@ using namespace mozilla::psm;
                             
 //#define DUMP_BUFFER  //Enable this define along with
                        //DEBUG_SSL_VERBOSE to dump SSL
                        //read/write buffer to a log.
                        //Uses PR_LOG except on Mac where
                        //we always write out to our own
                        //file.
 
+
 NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
-NSSCleanupAutoPtrClass(char, PL_strfree)
 NSSCleanupAutoPtrClass(void, PR_FREEIF)
 NSSCleanupAutoPtrClass_WithParam(PRArenaPool, PORT_FreeArena, FalseParam, false)
 
 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
 /* SSM_UserCertChoice: enum for cert choice info */
 typedef enum {ASK, AUTO} SSM_UserCertChoice;
 
@@ -145,66 +145,20 @@ void MyLogFunction(const char *fmt, ...)
       return;
   PR_vfprintf(gMyLogFile, fmt, ap);
   va_end(ap);
 }
 
 #define PR_LOG(module,level,args) MyLogFunction args
 #endif
 
-
-nsSSLSocketThreadData::nsSSLSocketThreadData()
-: mSSLState(ssl_idle)
-, mPRErrorCode(PR_SUCCESS)
-, mSSLDataBuffer(nsnull)
-, mSSLDataBufferAllocatedSize(0)
-, mSSLRequestedTransferAmount(0)
-, mSSLRemainingReadResultData(nsnull)
-, mSSLResultRemainingBytes(0)
-, mReplacedSSLFileDesc(nsnull)
-, mOneBytePendingFromEarlierWrite(false)
-, mThePendingByte(0)
-, mOriginalRequestedTransferAmount(0)
-{
-}
-
-nsSSLSocketThreadData::~nsSSLSocketThreadData()
-{
-  NS_ASSERTION(mSSLState != ssl_pending_write
-               &&
-               mSSLState != ssl_pending_read, 
-               "oops??? ssl socket is not idle at the time it is being destroyed");
-  if (mSSLDataBuffer) {
-    nsMemory::Free(mSSLDataBuffer);
-  }
-}
-
-bool nsSSLSocketThreadData::ensure_buffer_size(PRInt32 amount)
-{
-  if (amount > mSSLDataBufferAllocatedSize) {
-    if (mSSLDataBuffer) {
-      mSSLDataBuffer = (char*)nsMemory::Realloc(mSSLDataBuffer, amount);
-    }
-    else {
-      mSSLDataBuffer = (char*)nsMemory::Alloc(amount);
-    }
-    
-    if (!mSSLDataBuffer)
-      return false;
-
-    mSSLDataBufferAllocatedSize = amount;
-  }
-  
-  return true;
-}
-
 nsNSSSocketInfo::nsNSSSocketInfo()
   : mMutex("nsNSSSocketInfo::nsNSSSocketInfo"),
     mFd(nsnull),
-    mBlockingState(blocking_state_unknown),
+    mCertVerificationState(before_cert_verification),
     mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
     mSubRequestsHighSecurity(0),
     mSubRequestsLowSecurity(0),
     mSubRequestsBrokenSecurity(0),
     mSubRequestsNoSecurity(0),
     mErrorCode(0),
     mErrorMessageType(PlainErrorMessage),
     mForSTARTTLS(false),
@@ -212,23 +166,20 @@ nsNSSSocketInfo::nsNSSSocketInfo()
     mHasCleartextPhase(false),
     mHandshakeInProgress(false),
     mAllowTLSIntoleranceTimeout(true),
     mRememberClientAuthCertificate(false),
     mHandshakeStartTime(0),
     mPort(0),
     mIsCertIssuerBlacklisted(false)
 {
-  mThreadData = new nsSSLSocketThreadData;
 }
 
 nsNSSSocketInfo::~nsNSSSocketInfo()
 {
-  delete mThreadData;
-
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return;
 
   shutdown(calledFromObject);
 }
 
 void nsNSSSocketInfo::virtualDestroyNSSReference()
@@ -292,17 +243,17 @@ nsNSSSocketInfo::GetErrorCode() const
 {
   MutexAutoLock lock(mMutex);
 
   return mErrorCode;
 }
 
 void
 nsNSSSocketInfo::SetCanceled(PRErrorCode errorCode,
-                             ErrorMessageType errorMessageType)
+                             SSLErrorMessageType errorMessageType)
 {
   MutexAutoLock lock(mMutex);
 
   mErrorCode = errorCode;
   mErrorMessageType = errorMessageType;
   mErrorMessageCached.Truncate();
 }
 
@@ -341,17 +292,16 @@ NS_IMETHODIMP
 nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
 {
   if (!aCallbacks) {
     mCallbacks = nsnull;
     return NS_OK;
   }
 
   mCallbacks = aCallbacks;
-  mDocShellDependentStuffKnown = false;
 
   return NS_OK;
 }
 
 static void
 getSecureBrowserUI(nsIInterfaceRequestor * callbacks,
                    nsISecureBrowserUI ** result)
 {
@@ -540,19 +490,16 @@ NS_IMETHODIMP nsNSSSocketInfo::GetInterf
   nsresult rv;
   if (!mCallbacks) {
     nsCOMPtr<nsIInterfaceRequestor> ir = new PipUIContext();
     if (!ir)
       return NS_ERROR_OUT_OF_MEMORY;
 
     rv = ir->GetInterface(uuid, result);
   } else {
-    if (nsSSLThread::stoppedOrStopping())
-      return NS_ERROR_FAILURE;
-
     rv = mCallbacks->GetInterface(uuid, result);
   }
   return rv;
 }
 
 nsresult
 nsNSSSocketInfo::GetForSTARTTLS(bool* aForSTARTTLS)
 {
@@ -817,20 +764,20 @@ nsNSSSocketInfo::GetClassIDNoAlloc(nsCID
 }
 
 nsresult nsNSSSocketInfo::ActivateSSL()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
-  nsresult rv = nsSSLThread::requestActivateSSL(this);
-  
-  if (NS_FAILED(rv))
-    return rv;
+  if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, true))
+    return NS_ERROR_FAILURE;		
+  if (SECSuccess != SSL_ResetHandshake(mFd, false))
+    return NS_ERROR_FAILURE;
 
   mHandshakePending = true;
 
   return NS_OK;
 }
 
 nsresult nsNSSSocketInfo::GetFileDescPtr(PRFileDesc** aFilePtr)
 {
@@ -882,16 +829,54 @@ void nsNSSSocketInfo::GetPreviousCert(ns
   }
 
   nsRefPtr<PreviousCertRunnable> runnable = new PreviousCertRunnable(mCallbacks);
   nsresult rv = runnable->DispatchToMainThreadAndWait();
   NS_ASSERTION(NS_SUCCEEDED(rv), "runnable->DispatchToMainThreadAndWait() failed");
   runnable->mPreviousCert.forget(_result);
 }
 
+void
+nsNSSSocketInfo::SetCertVerificationWaiting()
+{
+  // mCertVerificationState may be before_cert_verification for the first
+  // handshake on the connection, or after_cert_verification for subsequent
+  // renegotiation handshakes.
+  NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
+               "Invalid state transition to waiting_for_cert_verification");
+  mCertVerificationState = waiting_for_cert_verification;
+}
+
+void
+nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
+                                           SSLErrorMessageType errorMessageType)
+{
+  NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
+               "Invalid state transition to cert_verification_finished");
+
+  if (errorCode != 0) {
+    SetCanceled(errorCode, errorMessageType);
+  } else if (mFd) {
+    // We haven't closed the connection already, so restart it
+    SECStatus rv = SSL_RestartHandshakeAfterAuthCertificate(mFd);
+    if (rv != SECSuccess) {
+      errorCode = PR_GetError();
+      if (errorCode == 0) {
+        NS_ERROR("SSL_RestartHandshakeAfterAuthCertificate didn't set error code");
+        errorCode = PR_INVALID_STATE_ERROR;
+      }
+      SetCanceled(errorCode, PlainErrorMessage);
+    }
+  } else {
+    // If we closed the connection alreay, we don't have anything to do
+  }
+
+  mCertVerificationState = after_cert_verification;
+}
+
 nsresult nsNSSSocketInfo::GetSSLStatus(nsISSLStatus** _result)
 {
   NS_ENSURE_ARG_POINTER(_result);
 
   *_result = mSSLStatus;
   NS_IF_ADDREF(*_result);
 
   return NS_OK;
@@ -942,19 +927,16 @@ void nsSSLIOLayerHelpers::Cleanup()
     mTLSTolerantSites = nsnull;
   }
 
   if (mRenegoUnrestrictedSites) {
     delete mRenegoUnrestrictedSites;
     mRenegoUnrestrictedSites = nsnull;
   }
 
-  if (mSharedPollableEvent)
-    PR_DestroyPollableEvent(mSharedPollableEvent);
-
   if (mutex) {
     delete mutex;
     mutex = nsnull;
   }
 
   if (mHostsWithCertErrors) {
     delete mHostsWithCertErrors;
     mHostsWithCertErrors = nsnull;
@@ -1427,29 +1409,26 @@ formatOverridableCertErrorMessage(nsISSL
 static void
 nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRErrorCode err)
 {
   if (!NS_IsMainThread()) {
     NS_ERROR("nsHandleSSLError called off the main thread");
     return;
   }
 
-  // SetCanceled is only called by the main thread or the SSL thread. Whenever
-  // this function is called, the SSL thread is waiting on this thread (the
-  // main thread). So, no mutex is necessary for SetCanceled()/GetError*().
+  // SetCanceled is only called by the main thread or the socket transport
+  // thread. Whenever this function is called on the main thread, the SSL
+  // thread is blocked on it. So, no mutex is necessary for
+  // SetCanceled()/GetError*().
   if (socketInfo->GetErrorCode()) {
     // If the socket has been flagged as canceled,
     // the code who did was responsible for setting the error code.
     return;
   }
 
-  if (nsSSLThread::stoppedOrStopping()) {
-    return;
-  }
-
   nsresult rv;
   NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
   if (NS_FAILED(rv))
     return;
 
   nsXPIDLCString hostName;
   socketInfo->GetHostName(getter_Copies(hostName));
@@ -1468,71 +1447,69 @@ nsHandleSSLError(nsNSSSocketInfo *socket
       hostWithPortString.AppendLiteral(":");
       hostWithPortString.AppendInt(port);
     
       bool suppressMessage = false; // obsolete, ignored
       rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage);
     }
   }
 
-  socketInfo->SetCanceled(err, nsNSSSocketInfo::PlainErrorMessage);
+  socketInfo->SetCanceled(err, PlainErrorMessage);
 }
 
+namespace {
+
+nsNSSSocketInfo *
+getSocketInfoIfRunning(PRFileDesc * fd,
+                       const nsNSSShutDownPreventionLock & /*proofOfLock*/)
+{
+  if (!fd || !fd->lower || !fd->secret ||
+      fd->identity != nsSSLIOLayerHelpers::nsSSLIOLayerIdentity) {
+    NS_ERROR("bad file descriptor passed to getSocketInfoIfRunning");
+    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+    return nsnull;
+  }
+
+  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
+
+  if (socketInfo->isAlreadyShutDown() || socketInfo->isPK11LoggedOut()) {
+    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
+    return nsnull;
+  }
+
+  if (socketInfo->GetErrorCode()) {
+    PRErrorCode err = socketInfo->GetErrorCode();
+    // If we get here, it is probably because cert verification failed and this
+    // is the first I/O attempt since that failure.
+    PR_SetError(err, 0);
+    return nsnull;
+  }
+
+  return socketInfo;
+}
+
+} // unnnamed namespace
+
 static PRStatus PR_CALLBACK
 nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
                     PRIntervalTime timeout)
 {
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] connecting SSL socket\n", (void*)fd));
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower)
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
   
-  PRStatus status = PR_SUCCESS;
-
-#if defined(XP_BEOS)
-  // Due to BeOS net_server's lack of support for nonblocking sockets,
-  // we must execute this entire connect as a blocking operation - bug 70217
- 
-  PRSocketOptionData sockopt;
-  sockopt.option = PR_SockOpt_Nonblocking;
-  PR_GetSocketOption(fd, &sockopt);
-  bool oldBlockVal = sockopt.value.non_blocking;
-  sockopt.option = PR_SockOpt_Nonblocking;
-  sockopt.value.non_blocking = false;
-  PR_SetSocketOption(fd, &sockopt);
-#endif
-  
-  status = fd->lower->methods->connect(fd->lower, addr, 
-#if defined(XP_BEOS)  // bug 70217
-                                       PR_INTERVAL_NO_TIMEOUT);
-#else
-                                       timeout);
-#endif
+  PRStatus status = fd->lower->methods->connect(fd->lower, addr, timeout);
   if (status != PR_SUCCESS) {
     PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("[%p] Lower layer connect error: %d\n",
                                       (void*)fd, PR_GetError()));
-#if defined(XP_BEOS)  // bug 70217
-    goto loser;
-#else
     return status;
-#endif
   }
-  
+
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Connect\n", (void*)fd));
-
-#if defined(XP_BEOS)  // bug 70217
- loser:
-  // We put the Nonblocking bit back to the value it was when 
-  // we entered this function.
-  NS_ASSERTION(sockopt.option == PR_SockOpt_Nonblocking,
-               "sockopt.option was re-set to an unexpected value");
-  sockopt.value.non_blocking = oldBlockVal;
-  PR_SetSocketOption(fd, &sockopt);
-#endif
-
   return status;
 }
 
 // nsPSMRememberCertErrorsTable
 
 nsPSMRememberCertErrorsTable::nsPSMRememberCertErrorsTable()
 {
   mErrorHosts.Init(16);
@@ -1688,28 +1665,34 @@ nsSSLIOLayerClose(PRFileDesc *fd)
   if (!fd)
     return PR_FAILURE;
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Shutting down socket\n", (void*)fd));
   
   nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
   NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
 
-  return nsSSLThread::requestClose(socketInfo);
+  return socketInfo->CloseSocketAndDestroy(locker);
 }
 
-PRStatus nsNSSSocketInfo::CloseSocketAndDestroy()
+PRStatus nsNSSSocketInfo::CloseSocketAndDestroy(
+  const nsNSSShutDownPreventionLock & /*proofOfLock*/)
 {
-  nsNSSShutDownPreventionLock locker;
-
   nsNSSShutDownList::trackSSLSocketClose();
 
   PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER);
 
   PRStatus status = mFd->methods->close(mFd);
+  
+  // the nsNSSSocketInfo instance can out-live the connection, so we need some
+  // indication that the connection has been closed. mFd == nsnull is that
+  // indication. This is needed, for example, when the connection is closed
+  // before we have finished validating the server's certificate.
+  mFd = nsnull;
+  
   if (status != PR_SUCCESS) return status;
 
   popped->identity = PR_INVALID_IO_LAYER;
   NS_RELEASE_THIS();
   popped->dtor(popped);
 
   return PR_SUCCESS;
 }
@@ -1837,21 +1820,19 @@ class SSLErrorRunnable : public SyncRunn
   {
     nsHandleSSLError(mInfoObject, mErrorCode);
   }
   
   nsRefPtr<nsNSSSocketInfo> mInfoObject;
   const PRErrorCode mErrorCode;
 };
 
-PRInt32
-nsSSLThread::checkHandshake(PRInt32 bytesTransfered, 
-                            bool wasReading,
-                            PRFileDesc* ssl_layer_fd, 
-                            nsNSSSocketInfo *socketInfo)
+PRInt32 checkHandshake(PRInt32 bytesTransfered, bool wasReading,
+                       PRFileDesc* ssl_layer_fd,
+                       nsNSSSocketInfo *socketInfo)
 {
   // This is where we work around all of those SSL servers that don't 
   // conform to the SSL spec and shutdown a connection when we request
   // SSL v3.1 (aka TLS).  The spec says the client says what version
   // of the protocol we're willing to perform, in our case SSL v3.1
   // In its response, the server says which version it wants to perform.
   // Many servers out there only know how to do v3.0.  Next, we're supposed
   // to send back the version of the protocol we requested (ie v3.1).  At
@@ -1895,19 +1876,27 @@ nsSSLThread::checkHandshake(PRInt32 byte
 
       if (!wantRetry // no decision yet
           && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
       {
         wantRetry = nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo);
       }
     }
     
-    // This is the common place where we trigger an error message on a SSL socket.
-    // This might be reached at any time of the connection.
-    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err))) {
+    // This is the common place where we trigger non-cert-errors on a SSL
+    // socket. This might be reached at any time of the connection.
+    //
+    // The socketInfo->GetErrorCode() check is here to ensure we don't try to
+    // do the synchronous dispatch to the main thread unnecessarily after we've
+    // already handled a certificate error. (SSLErrorRunnable calls
+    // nsHandleSSLError, which has logic to avoid replacing the error message,
+    // so without the !socketInfo->GetErrorCode(), it would just be an
+    // expensive no-op.)
+    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err)) &&
+        !socketInfo->GetErrorCode()) {
       nsRefPtr<SyncRunnableBase> runnable = new SSLErrorRunnable(socketInfo,
                                                                  err);
       (void) runnable->DispatchToMainThreadAndWait();
     }
   }
   else if (wasReading && 0 == bytesTransfered) // zero bytes on reading, socket closed
   {
     if (handleHandshakeResultNow)
@@ -1935,54 +1924,79 @@ nsSSLThread::checkHandshake(PRInt32 byte
     socketInfo->SetHandshakePending(false);
     socketInfo->SetHandshakeInProgress(false);
   }
   
   return bytesTransfered;
 }
 
 static PRInt16 PR_CALLBACK
-nsSSLIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+nsSSLIOLayerPoll(PRFileDesc * fd, PRInt16 in_flags, PRInt16 *out_flags)
 {
-  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] polling SSL socket\n", (void*)fd));
   nsNSSShutDownPreventionLock locker;
 
   if (!out_flags)
   {
     NS_WARNING("nsSSLIOLayerPoll called with null out_flags");
     return 0;
   }
 
   *out_flags = 0;
 
-  if (!fd)
-  {
-    NS_WARNING("nsSSLIOLayerPoll called with null fd");
-    return 0;
+  nsNSSSocketInfo * socketInfo = getSocketInfoIfRunning(fd, locker);
+  if (!socketInfo) {
+    // If we get here, it is probably because certificate validation failed
+    // and this is the first I/O operation after the failure. 
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+            ("[%p] polling SSL socket right after certificate verification failed "
+                  "or NSS shutdown or SDR logout %d\n",
+             fd, (int) in_flags));
+
+    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
+                 "caller did not poll for EXCEPT (canceled)");
+    // Since this poll method cannot return errors, we want the caller to call
+    // PR_Send/PR_Recv right away to get the error, so we tell that we are
+    // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning). 
+    *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619 
+    return in_flags;
   }
 
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestPoll(socketInfo, in_flags, out_flags);
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+         (socketInfo->IsWaitingForCertVerification()
+            ?  "[%p] polling SSL socket during certificate verification using lower %d\n"
+            :  "[%p] poll SSL socket using lower %d\n",
+         fd, (int) in_flags));
+
+  if (socketInfo->HandshakeTimeout()) {
+    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
+                 "caller did not poll for EXCEPT (handshake timeout)");
+    *out_flags = in_flags | PR_POLL_EXCEPT;
+    return in_flags;
+  }
+
+  // We want the handshake to continue during certificate validation, so we
+  // don't need to do anything special here. libssl automatically blocks when
+  // it reaches any point that would be unsafe to send/receive something before
+  // cert validation is complete.
+  PRInt16 result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] poll SSL socket returned %d\n",
+                                    (void*)fd, (int) result));
+  return result;
 }
 
 bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
 PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
 PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
 Mutex *nsSSLIOLayerHelpers::mutex = nsnull;
 nsCStringHashSet *nsSSLIOLayerHelpers::mTLSIntolerantSites = nsnull;
 nsCStringHashSet *nsSSLIOLayerHelpers::mTLSTolerantSites = nsnull;
 nsPSMRememberCertErrorsTable *nsSSLIOLayerHelpers::mHostsWithCertErrors = nsnull;
 nsCStringHashSet *nsSSLIOLayerHelpers::mRenegoUnrestrictedSites = nsnull;
 bool nsSSLIOLayerHelpers::mTreatUnsafeNegotiationAsBroken = false;
 PRInt32 nsSSLIOLayerHelpers::mWarnLevelMissingRFC5746 = 1;
-PRFileDesc *nsSSLIOLayerHelpers::mSharedPollableEvent = nsnull;
-nsNSSSocketInfo *nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull;
-bool nsSSLIOLayerHelpers::mPollableEventCurrentlySet = false;
 
 static PRIntn _PSM_InvalidInt(void)
 {
     PR_ASSERT(!"I/O method is invalid");
     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
     return -1;
 }
 
@@ -2005,109 +2019,100 @@ static PRFileDesc *_PSM_InvalidDesc(void
     PR_ASSERT(!"I/O method is invalid");
     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
     return NULL;
 }
 
 static PRStatus PR_CALLBACK PSMGetsockname(PRFileDesc *fd, PRNetAddr *addr)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestGetsockname(socketInfo, addr);
+
+  return fd->lower->methods->getsockname(fd->lower, addr);
 }
 
 static PRStatus PR_CALLBACK PSMGetpeername(PRFileDesc *fd, PRNetAddr *addr)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestGetpeername(socketInfo, addr);
+
+  return fd->lower->methods->getpeername(fd->lower, addr);
 }
 
 static PRStatus PR_CALLBACK PSMGetsocketoption(PRFileDesc *fd, 
                                         PRSocketOptionData *data)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestGetsocketoption(socketInfo, data);
+
+  return fd->lower->methods->getsocketoption(fd, data);
 }
 
 static PRStatus PR_CALLBACK PSMSetsocketoption(PRFileDesc *fd, 
                                         const PRSocketOptionData *data)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestSetsocketoption(socketInfo, data);
+
+  return fd->lower->methods->setsocketoption(fd, data);
 }
 
 static PRInt32 PR_CALLBACK PSMRecv(PRFileDesc *fd, void *buf, PRInt32 amount,
     PRIntn flags, PRIntervalTime timeout)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
-    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+  nsNSSSocketInfo *socketInfo = getSocketInfoIfRunning(fd, locker);
+  if (!socketInfo)
     return -1;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  if (flags == PR_MSG_PEEK) {
-    return nsSSLThread::requestRecvMsgPeek(socketInfo, buf, amount, flags, timeout);
-  }
-
-  if (flags != 0) {
+
+  if (flags != PR_MSG_PEEK && flags != 0) {
     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     return -1;
   }
 
-  return nsSSLThread::requestRead(socketInfo, buf, amount, timeout);
+  PRInt32 bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
+                                               timeout);
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)fd, bytesRead));
+
+#ifdef DEBUG_SSL_VERBOSE
+  DEBUG_DUMP_BUFFER((unsigned char*)buf, bytesRead);
+#endif
+
+  return checkHandshake(bytesRead, true, fd, socketInfo);
 }
 
 static PRInt32 PR_CALLBACK PSMSend(PRFileDesc *fd, const void *buf, PRInt32 amount,
     PRIntn flags, PRIntervalTime timeout)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
-    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+  nsNSSSocketInfo *socketInfo = getSocketInfoIfRunning(fd, locker);
+  if (!socketInfo)
     return -1;
-  }
 
   if (flags != 0) {
     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     return -1;
   }
 
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestWrite(socketInfo, buf, amount, timeout);
+#ifdef DEBUG_SSL_VERBOSE
+  DEBUG_DUMP_BUFFER((unsigned char*)buf, amount);
+#endif
+
+  PRInt32 bytesWritten = fd->lower->methods->send(fd->lower, buf, amount,
+                                                  flags, timeout);
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n",
+         fd, bytesWritten));
+
+  return checkHandshake(bytesWritten, false, fd, socketInfo);
 }
 
 static PRInt32 PR_CALLBACK
 nsSSLIOLayerRead(PRFileDesc* fd, void* buf, PRInt32 amount)
 {
   return PSMRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
 }
 
@@ -2115,24 +2120,21 @@ static PRInt32 PR_CALLBACK
 nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, PRInt32 amount)
 {
   return PSMSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
 }
 
 static PRStatus PR_CALLBACK PSMConnectcontinue(PRFileDesc *fd, PRInt16 out_flags)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker)) {
     return PR_FAILURE;
   }
 
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestConnectcontinue(socketInfo, out_flags);
+  return fd->lower->methods->connectcontinue(fd, out_flags);
 }
 
 nsresult nsSSLIOLayerHelpers::Init()
 {
   if (!nsSSLIOLayerInitialized) {
     nsSSLIOLayerInitialized = true;
     nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
     nsSSLIOLayerMethods  = *PR_GetDefaultIOMethods();
@@ -2167,20 +2169,16 @@ nsresult nsSSLIOLayerHelpers::Init()
     nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
     nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
     nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
     nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
   }
 
   mutex = new Mutex("nsSSLIOLayerHelpers.mutex");
 
-  mSharedPollableEvent = PR_NewPollableEvent();
-
-  // if we can not get a pollable event, we'll have to do busy waiting
-
   mTLSIntolerantSites = new nsCStringHashSet();
   if (!mTLSIntolerantSites)
     return NS_ERROR_OUT_OF_MEMORY;
 
   mTLSIntolerantSites->Init(1);
 
   mTLSTolerantSites = new nsCStringHashSet();
   if (!mTLSTolerantSites)
@@ -2840,17 +2838,17 @@ SECStatus nsNSS_SSLGetClientAuthData(voi
     new ClientAuthDataRunnable(caNames, pRetCert, pRetKey, info, serverCert);
   nsresult rv = runnable->DispatchToMainThreadAndWait();
   if (NS_FAILED(rv)) {
     PR_SetError(SEC_ERROR_NO_MEMORY, 0);
     return SECFailure;
   }
   
   if (runnable->mRV != SECSuccess) {
-    PORT_SetError(runnable->mErrorCodeToReport);
+    PR_SetError(runnable->mErrorCodeToReport, 0);
   }
 
   return runnable->mRV;
 }
 
 void ClientAuthDataRunnable::RunOnTargetThread()
 {
   PRArenaPool* arena = NULL;
@@ -3270,329 +3268,321 @@ done:
 }
 
 class CertErrorRunnable : public SyncRunnableBase
 {
  public:
   CertErrorRunnable(const void * fdForLogging,
                     nsIX509Cert * cert,
                     nsNSSSocketInfo * infoObject,
-                    const CERTVerifyLog * verify_log,
-                    bool hasCertNameMismatch,
-                    PRErrorCode defaultErrorCodeToReport)
+                    PRErrorCode defaultErrorCodeToReport,
+                    PRUint32 collectedErrors,
+                    PRErrorCode errorCodeTrust,
+                    PRErrorCode errorCodeMismatch,
+                    PRErrorCode errorCodeExpired)
     : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject),
-      mVerifyLog(verify_log), mHasCertNameMismatch(hasCertNameMismatch),
-      mRv(SECFailure), mErrorCodeToReport(defaultErrorCodeToReport)
+      mDefaultErrorCodeToReport(defaultErrorCodeToReport),
+      mCollectedErrors(collectedErrors),
+      mErrorCodeTrust(errorCodeTrust),
+      mErrorCodeMismatch(errorCodeMismatch),
+      mErrorCodeExpired(errorCodeExpired)
   {
   }
 
+  NS_DECL_NSIRUNNABLE
   virtual void RunOnTargetThread();
+  nsCOMPtr<nsIRunnable> mResult; // out
+private:
+  SSLServerCertVerificationResult* CheckCertOverrides();
   
-  // in
-  const void * const mFdForLogging;
-  nsCOMPtr<nsIX509Cert> mCert;
-  nsNSSSocketInfo * const mInfoObject;
-  const CERTVerifyLog * const mVerifyLog;
-  const bool mHasCertNameMismatch;
-  nsXPIDLCString mHostname;
-
-  SECStatus mRv; // out
-  PRErrorCode mErrorCodeToReport; // in/out
+  const void * const mFdForLogging; // may become an invalid pointer; do not dereference
+  const nsCOMPtr<nsIX509Cert> mCert;
+  const nsRefPtr<nsNSSSocketInfo> mInfoObject;
+  const PRErrorCode mDefaultErrorCodeToReport;
+  const PRUint32 mCollectedErrors;
+  const PRErrorCode mErrorCodeTrust;
+  const PRErrorCode mErrorCodeMismatch;
+  const PRErrorCode mErrorCodeExpired;
 };
 
-static SECStatus
-cancel_and_failure(PRErrorCode errorCode, nsNSSSocketInfo* infoObject)
-{
-  infoObject->SetCanceled(errorCode, nsNSSSocketInfo::PlainErrorMessage);
-  PR_SetError(errorCode, 0);
-  return SECFailure;
-}
-
-static SECStatus
-nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
+namespace mozilla { namespace psm {
+
+// Returns SECSuccess if it dispatched the CertErrorRunnable. In that case,
+// the caller should NOT dispatch its own SSLServerCertVerificationResult;
+// the CertErrorRunnable will do it instead.
+//
+// Returns SECFailure with the error code set if it does not dispatch the
+// CertErrorRunnable. In that case, the caller should dispatch its own 
+// SSLServerCertVerificationResult with the error code from PR_GetError().
+SECStatus
+HandleBadCertificate(PRErrorCode defaultErrorCodeToReport,
+                    nsNSSSocketInfo * socketInfo, CERTCertificate & cert,
+                    const void * fdForLogging,
+                    const nsNSSShutDownPreventionLock & /*proofOfLock*/)
 {
   // cert was revoked, don't do anything else
-  PRErrorCode defaultErrorCodeToReport = PR_GetError();
-  if (defaultErrorCodeToReport == SEC_ERROR_REVOKED_CERTIFICATE)
+  if (defaultErrorCodeToReport == SEC_ERROR_REVOKED_CERTIFICATE) {
+    PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
     return SECFailure;
+  }
 
   if (defaultErrorCodeToReport == 0) {
     NS_ERROR("No error code set during certificate validation failure.");
-    defaultErrorCodeToReport = SEC_ERROR_CERT_NOT_VALID;
-  }
-
-  nsNSSShutDownPreventionLock locker;
-  nsNSSSocketInfo* infoObject = (nsNSSSocketInfo *)arg;
-  if (!infoObject)
+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
     return SECFailure;
-
-  if (nsSSLThread::stoppedOrStopping())
-    return cancel_and_failure(PR_INVALID_STATE_ERROR, infoObject);
-
-  CERTCertificate *peerCert = nsnull;
-  CERTCertificateCleaner peerCertCleaner(peerCert);
-  peerCert = SSL_PeerCertificate(sslSocket);
-  if (!peerCert)
-    return cancel_and_failure(SEC_ERROR_NO_MEMORY, infoObject);
+  }
 
   nsRefPtr<nsNSSCertificate> nssCert;
-  nssCert = nsNSSCertificate::Create(peerCert);
-  if (!nssCert)
-    return cancel_and_failure(SEC_ERROR_NO_MEMORY, infoObject);
+  nssCert = nsNSSCertificate::Create(&cert);
+  if (!nssCert) {
+    NS_ERROR("nsNSSCertificate::Create failed in DispatchCertErrorRunnable");
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return SECFailure;
+  }
 
   SECStatus srv;
   nsresult nsrv;
 
   nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
-  if (!inss)
-    return cancel_and_failure(SEC_ERROR_NO_MEMORY, infoObject);
+  if (!inss) {
+    NS_ERROR("do_GetService(kNSSComponentCID) failed in DispatchCertErrorRunnable");
+    PR_SetError(defaultErrorCodeToReport, 0);
+    return SECFailure;
+  }
+
   nsRefPtr<nsCERTValInParamWrapper> survivingParams;
   nsrv = inss->GetDefaultCERTValInParam(survivingParams);
-  if (NS_FAILED(nsrv))
-    return cancel_and_failure(PR_INVALID_STATE_ERROR, infoObject);
+  if (NS_FAILED(nsrv)) {
+    NS_ERROR("GetDefaultCERTValInParam failed in DispatchCertErrorRunnable");
+    PR_SetError(defaultErrorCodeToReport, 0);
+    return SECFailure;
+  }
   
-  char *hostname = SSL_RevealURL(sslSocket);
-  if (!hostname)
-    return cancel_and_failure(SEC_ERROR_NO_MEMORY, infoObject);
-
-  charCleaner hostnameCleaner(hostname); 
+  PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+  PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
+  if (!log_arena) {
+    NS_ERROR("PORT_NewArena failed in DispatchCertErrorRunnable");
+    return SECFailure; // PORT_NewArena set error code
+  }
+
+  CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
+  if (!verify_log) {
+    NS_ERROR("PORT_ArenaZNew failed in DispatchCertErrorRunnable");
+    return SECFailure; // PORT_ArenaZNew set error code
+  }
+  CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
+  verify_log->arena = log_arena;
+
+  if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
+    srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), &cert,
+                                true, certificateUsageSSLServer,
+                                PR_Now(), static_cast<void*>(socketInfo),
+                                verify_log, NULL);
+  }
+  else {
+    CERTValOutParam cvout[2];
+    cvout[0].type = cert_po_errorLog;
+    cvout[0].value.pointer.log = verify_log;
+    cvout[1].type = cert_po_end;
+
+    srv = CERT_PKIXVerifyCert(&cert, certificateUsageSSLServer,
+                              survivingParams->GetRawPointerForNSS(),
+                              cvout, static_cast<void*>(socketInfo));
+  }
+
+  // We ignore the result code of the cert verification.
+  // Either it is a failure, which is expected, and we'll process the
+  //                         verify log below.
+  // Or it is a success, then a domain mismatch is the only 
+  //                     possible failure. 
+
+  PRErrorCode errorCodeMismatch = 0;
+  PRErrorCode errorCodeTrust = 0;
+  PRErrorCode errorCodeExpired = 0;
+
+  PRUint32 collected_errors = 0;
+
+  if (socketInfo->IsCertIssuerBlacklisted()) {
+    collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
+    errorCodeTrust = defaultErrorCodeToReport;
+  }
 
   // Check the name field against the desired hostname.
-  bool hasCertNameMismatch =
-      hostname[0] && CERT_VerifyCertName(peerCert, hostname) != SECSuccess;
-
+  if (CERT_VerifyCertName(&cert, socketInfo->GetHostName()) != SECSuccess) {
+    collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
+    errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
+  }
+
+  CERTVerifyLogNode *i_node;
+  for (i_node = verify_log->head; i_node; i_node = i_node->next)
   {
-    PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if (!log_arena)    
-      return cancel_and_failure(SEC_ERROR_NO_MEMORY, infoObject);
-
-    PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
-
-    CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
-    if (!verify_log)
-      return cancel_and_failure(SEC_ERROR_NO_MEMORY, infoObject);
-
-    CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
-
-    verify_log->arena = log_arena;
-
-    if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
-      srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), peerCert,
-                                  true, certificateUsageSSLServer,
-                                  PR_Now(), (void*)infoObject, 
-                                  verify_log, NULL);
-    }
-    else {
-      CERTValOutParam cvout[2];
-      cvout[0].type = cert_po_errorLog;
-      cvout[0].value.pointer.log = verify_log;
-      cvout[1].type = cert_po_end;
-
-      srv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
-                                survivingParams->GetRawPointerForNSS(),
-                                cvout, (void*)infoObject);
+    switch (i_node->error)
+    {
+      case SEC_ERROR_UNKNOWN_ISSUER:
+      case SEC_ERROR_CA_CERT_INVALID:
+      case SEC_ERROR_UNTRUSTED_ISSUER:
+      case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+      case SEC_ERROR_UNTRUSTED_CERT:
+      case SEC_ERROR_INADEQUATE_KEY_USAGE:
+        // We group all these errors as "cert not trusted"
+        collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
+        if (errorCodeTrust == SECSuccess) {
+          errorCodeTrust = i_node->error;
+        }
+        break;
+      case SSL_ERROR_BAD_CERT_DOMAIN:
+        collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
+        if (errorCodeMismatch == SECSuccess) {
+          errorCodeMismatch = i_node->error;
+        }
+        break;
+      case SEC_ERROR_EXPIRED_CERTIFICATE:
+        collected_errors |= nsICertOverrideService::ERROR_TIME;
+        if (errorCodeExpired == SECSuccess) {
+          errorCodeExpired = i_node->error;
+        }
+        break;
+      default:
+        PR_SetError(i_node->error, 0);
+        return SECFailure;
     }
-
-    // We ignore the result code of the cert verification.
-    // Either it is a failure, which is expected, and we'll process the
-    //                         verify log below.
-    // Or it is a success, then a domain mismatch is the only 
-    //                     possible failure. 
-
-    nsRefPtr<CertErrorRunnable> runnable =
-      new CertErrorRunnable(static_cast<void*>(sslSocket), 
-                            static_cast<nsIX509Cert*>(nssCert.get()),
-                            infoObject, verify_log, hasCertNameMismatch,
-                            defaultErrorCodeToReport);
-
-    // now grab the host name to pass to the STS Service
-    nsrv = infoObject->GetHostName(getter_Copies(runnable->mHostname));
-    if (NS_FAILED(nsrv)) {
-      PR_SetError(defaultErrorCodeToReport, 0);
-      return SECFailure;
-    }
-
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-           ("[%p][%p] Before dispatching CertErrorRunnable\n",
-           sslSocket, runnable.get()));
-
-    // Dispatch SYNC since the result is used below
-    (void) runnable->DispatchToMainThreadAndWait();
-
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-           ("[%p][%p] After dispatching CertErrorRunnable\n",
-           sslSocket, runnable.get()));
-
-    if (runnable->mRv == SECSuccess)
-      return SECSuccess;
-  
-    NS_ASSERTION(runnable->mErrorCodeToReport != 0,
-                 "CertErrorRunnable did not set error code.");
-    PR_SetError(runnable->mErrorCodeToReport ? runnable->mErrorCodeToReport
-                                             : defaultErrorCodeToReport, 0);
+  }
+
+  if (!collected_errors)
+  {
+    // This will happen when CERT_*Verify* only returned error(s) that are
+    // not on our whitelist of overridable certificate errors.
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] !collected_errors: %d\n",
+           fdForLogging, static_cast<int>(defaultErrorCodeToReport)));
+    PR_SetError(defaultErrorCodeToReport, 0);
     return SECFailure;
   }
+
+  socketInfo->SetStatusErrorBits(*nssCert, collected_errors);
+
+  nsRefPtr<CertErrorRunnable> runnable =
+    new CertErrorRunnable(fdForLogging, 
+                          static_cast<nsIX509Cert*>(nssCert.get()),
+                          socketInfo, defaultErrorCodeToReport, 
+                          collected_errors, errorCodeTrust, 
+                          errorCodeMismatch, errorCodeExpired);
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+          ("[%p][%p] Before dispatching CertErrorRunnable\n",
+          fdForLogging, runnable.get()));
+
+  nsresult nrv;
+  nsCOMPtr<nsIEventTarget> stsTarget
+    = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
+  if (NS_SUCCEEDED(nrv)) {
+    nrv = stsTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  }
+  if (NS_FAILED(nrv)) {
+    NS_ERROR("Failed to dispatch CertErrorRunnable");
+    PR_SetError(defaultErrorCodeToReport, 0);
+    return SECFailure;
+  }
+
+  return SECSuccess;
 }
 
-void CertErrorRunnable::RunOnTargetThread()
+} } // namespace mozilla::psm
+
+void
+nsNSSSocketInfo::SetStatusErrorBits(nsIX509Cert & cert,
+                                    PRUint32 collected_errors)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (!mSSLStatus)
+    mSSLStatus = new nsSSLStatus();
+
+  mSSLStatus->mServerCert = &cert;
+
+  mSSLStatus->mHaveCertErrorBits = true;
+  mSSLStatus->mIsDomainMismatch = 
+    collected_errors & nsICertOverrideService::ERROR_MISMATCH;
+  mSSLStatus->mIsNotValidAtThisTime = 
+    collected_errors & nsICertOverrideService::ERROR_TIME;
+  mSSLStatus->mIsUntrusted = 
+    collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
+
+  nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
+    this, mSSLStatus, SECFailure);
+}
+
+SSLServerCertVerificationResult *
+CertErrorRunnable::CheckCertOverrides()
 {
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] top of CertErrorRunnable::Run\n",
                                     mFdForLogging, this));
 
   if (!NS_IsMainThread()) {
-    NS_ERROR("CertErrorRunnable::RunOnTargetThread called off main thread");
-    return;
-  }
-
-  if (nsSSLThread::stoppedOrStopping())
-    return;
- 
-  PRErrorCode errorCodeMismatch = 0;
-  PRErrorCode errorCodeTrust = 0;
-  PRErrorCode errorCodeExpired = 0;
-
-  PRUint32 collected_errors = 0;
-
-  if (mInfoObject->IsCertIssuerBlacklisted()) {
-    collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
-    errorCodeTrust = mErrorCodeToReport;
-  }
-
-  if (mHasCertNameMismatch) {
-    collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
-    errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
+    NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
+    return new SSLServerCertVerificationResult(*mInfoObject,
+                                               mDefaultErrorCodeToReport);
   }
 
-  {
-    CERTVerifyLogNode *i_node;
-    for (i_node = mVerifyLog->head; i_node; i_node = i_node->next)
-    {
-      switch (i_node->error)
-      {
-        case SEC_ERROR_UNKNOWN_ISSUER:
-        case SEC_ERROR_CA_CERT_INVALID:
-        case SEC_ERROR_UNTRUSTED_ISSUER:
-        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-        case SEC_ERROR_UNTRUSTED_CERT:
-        case SEC_ERROR_INADEQUATE_KEY_USAGE:
-          // We group all these errors as "cert not trusted"
-          collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
-          if (errorCodeTrust == SECSuccess) {
-            errorCodeTrust = i_node->error;
-          }
-          break;
-        case SSL_ERROR_BAD_CERT_DOMAIN:
-          collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
-          if (errorCodeMismatch == SECSuccess) {
-            errorCodeMismatch = i_node->error;
-          }
-          break;
-        case SEC_ERROR_EXPIRED_CERTIFICATE:
-          collected_errors |= nsICertOverrideService::ERROR_TIME;
-          if (errorCodeExpired == SECSuccess) {
-            errorCodeExpired = i_node->error;
-          }
-          break;
-        default:
-          // we are not willing to continue on any other error
-          nsHandleSSLError(mInfoObject, i_node->error);
-          // this error is our stop condition, so let's make sure
-          // this error code will be reported to the external world.
-          mErrorCodeToReport = i_node->error;
-          return;
-      }
-    }
-  }
-
-  if (!collected_errors)
-  {
-    NS_NOTREACHED("why did NSS call our bad cert handler if all looks good? Let's cancel the connection");
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] !collected_errors\n",
-           mFdForLogging, this));
-    return;
-  }
-
-  nsRefPtr<nsSSLStatus> status = mInfoObject->SSLStatus();
-  if (!status) {
-    status = new nsSSLStatus();
-    mInfoObject->SetSSLStatus(status);
-  }
-
-  if (status) {
-    if (!status->mServerCert) {
-      status->mServerCert = mCert;
-    }
-
-    status->mHaveCertErrorBits = true;
-    status->mIsDomainMismatch = collected_errors & nsICertOverrideService::ERROR_MISMATCH;
-    status->mIsNotValidAtThisTime = collected_errors & nsICertOverrideService::ERROR_TIME;
-    status->mIsUntrusted = collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
-
-    nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
-      mInfoObject, status, SECFailure);
-  }
-
-  nsDependentCString hostString(mHostname);
-
   PRInt32 port;
   mInfoObject->GetPort(&port);
 
-  nsCString hostWithPortString = hostString;
+  nsCString hostWithPortString;
+  hostWithPortString.AppendASCII(mInfoObject->GetHostName());
   hostWithPortString.AppendLiteral(":");
   hostWithPortString.AppendInt(port);
 
-  NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
-
-  PRUint32 remaining_display_errors = collected_errors;
+  PRUint32 remaining_display_errors = mCollectedErrors;
 
   nsresult nsrv;
 
   // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
   // connections must be dropped when there are any certificate errors
   // (STS Spec section 7.3).
   bool strictTransportSecurityEnabled = false;
   nsCOMPtr<nsIStrictTransportSecurityService> stss
     = do_GetService(NS_STSSERVICE_CONTRACTID, &nsrv);
   if (NS_SUCCEEDED(nsrv)) {
-    nsrv = stss->IsStsHost(mHostname, &strictTransportSecurityEnabled);
+    nsrv = stss->IsStsHost(mInfoObject->GetHostName(),
+                           &strictTransportSecurityEnabled);
   }
-  if (NS_FAILED(nsrv))
-    return; // use default rv and errorCodeToReport
+  if (NS_FAILED(nsrv)) {
+    return new SSLServerCertVerificationResult(*mInfoObject,
+                                               mDefaultErrorCodeToReport);
+  }
 
   if (!strictTransportSecurityEnabled) {
     nsCOMPtr<nsICertOverrideService> overrideService =
       do_GetService(NS_CERTOVERRIDE_CONTRACTID);
     // it is fine to continue without the nsICertOverrideService
 
     PRUint32 overrideBits = 0;
 
     if (overrideService)
     {
       bool haveOverride;
       bool isTemporaryOverride; // we don't care
-
+      nsCString hostString(mInfoObject->GetHostName());
       nsrv = overrideService->HasMatchingOverride(hostString, port,
                                                   mCert,
                                                   &overrideBits,
                                                   &isTemporaryOverride, 
                                                   &haveOverride);
       if (NS_SUCCEEDED(nsrv) && haveOverride) 
       {
-        // remove the errors that are already overriden
+       // remove the errors that are already overriden
         remaining_display_errors -= overrideBits;
       }
     }
 
     if (!remaining_display_errors) {
       // all errors are covered by override rules, so let's accept the cert
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
              ("[%p][%p] All errors covered by override rules\n",
              mFdForLogging, this));
-      mRv = SECSuccess;
-      return;
+      return new SSLServerCertVerificationResult(*mInfoObject, 0);
     }
   } else {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("[%p][%p] Strict-Transport-Security is violated: untrusted "
             "transport layer\n", mFdForLogging, this));
   }
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
@@ -3605,41 +3595,81 @@ void CertErrorRunnable::RunOnTargetThrea
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
   nsCOMPtr<nsIInterfaceRequestor> cb;
   mInfoObject->GetNotificationCallbacks(getter_AddRefs(cb));
   if (cb) {
     nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
     if (bcl) {
       nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(mInfoObject);
       bool suppressMessage = false; // obsolete, ignored
-      nsrv = bcl->NotifyCertProblem(csi, status, hostWithPortString,
-                                    &suppressMessage);
+      nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(),
+                                    hostWithPortString, &suppressMessage);
     }
   }
 
   nsCOMPtr<nsIRecentBadCertsService> recentBadCertsService = 
     do_GetService(NS_RECENTBADCERTS_CONTRACTID);
-
+ 
   if (recentBadCertsService) {
-    recentBadCertsService->AddBadCert(hostWithPortStringUTF16, status);
+    NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
+    recentBadCertsService->AddBadCert(hostWithPortStringUTF16,
+                                      mInfoObject->SSLStatus());
   }
 
   // pick the error code to report by priority
-  mErrorCodeToReport = 0;
-  if (remaining_display_errors & nsICertOverrideService::ERROR_UNTRUSTED)
-    mErrorCodeToReport = errorCodeTrust;
-  else if (remaining_display_errors & nsICertOverrideService::ERROR_MISMATCH)
-    mErrorCodeToReport = errorCodeMismatch;
-  else if (remaining_display_errors & nsICertOverrideService::ERROR_TIME)
-    mErrorCodeToReport = errorCodeExpired;
-
-  mInfoObject->SetCanceled(mErrorCodeToReport,
-                           nsNSSSocketInfo::OverridableCertErrorMessage);
+  PRErrorCode errorCodeToReport = mErrorCodeTrust    ? mErrorCodeTrust
+                                : mErrorCodeMismatch ? mErrorCodeMismatch
+                                : mErrorCodeExpired  ? mErrorCodeExpired
+                                : mDefaultErrorCodeToReport;
+
+  return new SSLServerCertVerificationResult(*mInfoObject, errorCodeToReport,
+                                             OverridableCertErrorMessage);
 }
 
+NS_IMETHODIMP
+CertErrorRunnable::Run()
+{
+  // This code is confusing: First, Run() is called on the socket transport
+  // thread. Then we re-dispatch it to the main thread synchronously (step 1).
+  // On the main thread, we call CheckCertOverrides (step 2). Then we return
+  // from the main thread and are back on the socket transport thread. There,
+  // we run the result runnable directly (step 3).
+  if (!NS_IsMainThread()) {
+    // We are running on the socket transport thread. We need to re-dispatch
+    // ourselves synchronously to the main thread.
+    DispatchToMainThreadAndWait(); // step 1
+
+    // step 3
+    if (!mResult) {
+      // Either the dispatch failed or CheckCertOverrides wrongly returned null
+      NS_ERROR("Did not create a SSLServerCertVerificationResult");
+      mResult = new SSLServerCertVerificationResult(*mInfoObject,
+                                                    PR_INVALID_STATE_ERROR);
+    }
+    return mResult->Run(); 
+  } else {
+    // block this thread (the socket transport thread) until RunOnTargetThread
+    // is complete.
+    return SyncRunnableBase::Run(); // step 2
+  }
+}
+
+void 
+CertErrorRunnable::RunOnTargetThread()
+{
+  // Now we are running on the main thread, blocking the socket tranposrt
+  // thread. This is exactly the state we need to be in to call
+  // CheckCertOverrides; CheckCertOverrides must block events on both of
+  // these threads because it calls nsNSSSocketInfo::GetInterface(),
+  // which calls nsHttpConnection::GetInterface() through
+  // nsNSSSocketInfo::mCallbacks. nsHttpConnection::GetInterface must always
+  // execute on the main thread, with the socket transport thread blocked.
+  mResult = CheckCertOverrides(); 
+}
+ 
 static PRFileDesc*
 nsSSLIOLayerImportFD(PRFileDesc *fd,
                      nsNSSSocketInfo *infoObject,
                      const char *host,
                      bool anonymousLoad)
 {
   nsNSSShutDownPreventionLock locker;
   PRFileDesc* sslSock = SSL_ImportFD(nsnull, fd);
@@ -3653,21 +3683,24 @@ nsSSLIOLayerImportFD(PRFileDesc *fd,
   // Disable this hook if we connect anonymously. See bug 466080.
   if (anonymousLoad) {
       SSL_GetClientAuthDataHook(sslSock, NULL, infoObject);
   } else {
       SSL_GetClientAuthDataHook(sslSock, 
                             (SSLGetClientAuthData)nsNSS_SSLGetClientAuthData,
                             infoObject);
   }
-  SSL_AuthCertificateHook(sslSock, AuthCertificateCallback, 0);
-
-  PRInt32 ret = SSL_SetURL(sslSock, host);
-  if (ret == -1) {
-    NS_ASSERTION(false, "NSS: Error setting server name");
+  if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook,
+                                            infoObject)) {
+    NS_NOTREACHED("failed to configure AuthCertificateHook");
+    goto loser;
+  }
+
+  if (SECSuccess != SSL_SetURL(sslSock, host)) {
+    NS_NOTREACHED("SSL_SetURL failed");
     goto loser;
   }
   return sslSock;
 loser:
   if (sslSock) {
     PR_Close(sslSock);
   }
   return nsnull;
@@ -3703,20 +3736,16 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, b
     // One advantage of this approach, if a site only supports the older
     // hellos, it is more likely that we will get a reasonable error code
     // on our single retry attempt.
   }
 
   if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
     return NS_ERROR_FAILURE;
   }
-  if (SECSuccess != SSL_BadCertHook(fd, (SSLBadCertHandler) nsNSSBadCertHandler,
-                                    infoObject)) {
-    return NS_ERROR_FAILURE;
-  }
   
   if (nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(nsDependentCString(host))) {
     if (SECSuccess != SSL_OptionSet(fd, SSL_REQUIRE_SAFE_NEGOTIATION, false)) {
       return NS_ERROR_FAILURE;
     }
     if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED)) {
       return NS_ERROR_FAILURE;
     }
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -55,82 +55,30 @@
 #include "nsIAssociatedContentSecurity.h"
 #include "nsXPIDLString.h"
 #include "nsNSSShutDown.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsAutoPtr.h"
 #include "nsNSSCertificate.h"
 #include "nsDataHashtable.h"
 
-class nsIChannel;
-class nsSSLThread;
-class ::mozilla::MutexAutoLock;
-
-/*
- * This class is used to store SSL socket I/O state information,
- * that is not being executed directly, but defered to 
- * the separate SSL thread.
- */
-class nsSSLSocketThreadData
-{
-public:
-  nsSSLSocketThreadData();
-  ~nsSSLSocketThreadData();
+namespace mozilla {
 
-  bool ensure_buffer_size(PRInt32 amount);
-  
-  enum ssl_state { 
-    ssl_invalid,       // used for initializating, should never occur
-    ssl_idle,          // not in use by SSL thread, no activity pending
-    ssl_pending_write, // waiting for SSL thread to complete writing
-    ssl_pending_read,  // waiting for SSL thread to complete reading
-    ssl_writing_done,  // SSL write completed, results are ready
-    ssl_reading_done   // SSL read completed, results are ready
-  };
-  
-  ssl_state mSSLState;
+class MutexAutoLock;
 
-  // Used to transport I/O error codes between SSL thread
-  // and initial caller thread.
-  PRErrorCode mPRErrorCode;
+namespace psm {
 
-  // A buffer used to transfer I/O data between threads
-  char *mSSLDataBuffer;
-  PRInt32 mSSLDataBufferAllocatedSize;
-
-  // The amount requested to read or write by the caller.
-  PRInt32 mSSLRequestedTransferAmount;
+enum SSLErrorMessageType {
+  OverridableCertErrorMessage  = 1, // for *overridable* certificate errors
+  PlainErrorMessage = 2             // all other errors (or "no error")
+};
 
-  // A pointer into our buffer, to the first byte
-  // that has not yet been delivered to the caller.
-  // Necessary, as the caller of the read function
-  // might request smaller chunks.
-  const char *mSSLRemainingReadResultData;
-  
-  // The caller previously requested to read or write.
-  // As the initial request to read or write is defered,
-  // the caller might (in theory) request smaller chunks
-  // in subsequent calls.
-  // This variable stores the amount of bytes successfully
-  // transfered, that have not yet been reported to the caller.
-  PRInt32 mSSLResultRemainingBytes;
+} // namespace psm
 
-  // When defering SSL read/write activity to another thread,
-  // we switch the SSL level file descriptor of the original
-  // layered file descriptor to a pollable event,
-  // so we can wake up the original caller of the I/O function
-  // as soon as data is ready.
-  // This variable is used to save the SSL level file descriptor,
-  // to allow us to restore the original file descriptor layering.
-  PRFileDesc *mReplacedSSLFileDesc;
-
-  bool mOneBytePendingFromEarlierWrite;
-  unsigned char mThePendingByte;
-  PRInt32 mOriginalRequestedTransferAmount;
-};
+} // namespace mozilla
 
 class nsNSSSocketInfo : public nsITransportSecurityInfo,
                         public nsISSLSocketControl,
                         public nsIInterfaceRequestor,
                         public nsISSLStatusProvider,
                         public nsIAssociatedContentSecurity,
                         public nsISerializable,
                         public nsIClassInfo,
@@ -159,71 +107,91 @@ public:
   nsresult GetForSTARTTLS(bool *aForSTARTTLS);
 
   nsresult GetFileDescPtr(PRFileDesc** aFilePtr);
   nsresult SetFileDescPtr(PRFileDesc* aFilePtr);
 
   nsresult GetHandshakePending(bool *aHandshakePending);
   nsresult SetHandshakePending(bool aHandshakePending);
 
+  const char * GetHostName() const {
+    return mHostName.get();
+  }
   nsresult GetHostName(char **aHostName);
   nsresult SetHostName(const char *aHostName);
 
   nsresult GetPort(PRInt32 *aPort);
   nsresult SetPort(PRInt32 aPort);
 
   void GetPreviousCert(nsIX509Cert** _result);
 
-  enum ErrorMessageType {
-    OverridableCertErrorMessage  = 1, // for *overridable* certificate errors
-    PlainErrorMessage = 2,            // all other errors
-  };
-  void SetCanceled(PRErrorCode errorCode, ErrorMessageType errorMessageType);
   PRErrorCode GetErrorCode() const;
+  void SetCanceled(PRErrorCode errorCode,
+                   ::mozilla::psm::SSLErrorMessageType errorMessageType);
   
   void SetHasCleartextPhase(bool aHasCleartextPhase);
   bool GetHasCleartextPhase();
   
   void SetHandshakeInProgress(bool aIsIn);
   bool GetHandshakeInProgress() { return mHandshakeInProgress; }
   bool HandshakeTimeout();
 
   void SetAllowTLSIntoleranceTimeout(bool aAllow);
 
   nsresult RememberCAChain(CERTCertList *aCertList);
 
   /* Set SSL Status values */
   nsresult SetSSLStatus(nsSSLStatus *aSSLStatus);
   nsSSLStatus* SSLStatus() { return mSSLStatus; }
-  
-  PRStatus CloseSocketAndDestroy();
+  void SetStatusErrorBits(nsIX509Cert & cert, PRUint32 collected_errors);
+
+  PRStatus CloseSocketAndDestroy(
+                const nsNSSShutDownPreventionLock & proofOfLock);
   
   bool IsCertIssuerBlacklisted() const {
     return mIsCertIssuerBlacklisted;
   }
   void SetCertIssuerBlacklisted() {
     mIsCertIssuerBlacklisted = true;
   }
+
+  // XXX: These are only used on for diagnostic purposes
+  enum CertVerificationState {
+    before_cert_verification,
+    waiting_for_cert_verification,
+    after_cert_verification
+  };
+  void SetCertVerificationWaiting();
+  // Use errorCode == 0 to indicate success; in that case, errorMessageType is
+  // ignored.
+  void SetCertVerificationResult(PRErrorCode errorCode,
+              ::mozilla::psm::SSLErrorMessageType errorMessageType);
+  
+  // for logging only
+  PRBool IsWaitingForCertVerification() const
+  {
+    return mCertVerificationState == waiting_for_cert_verification;
+  }
+  
+
 protected:
   mutable ::mozilla::Mutex mMutex;
 
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   PRFileDesc* mFd;
-  enum { 
-    blocking_state_unknown, is_nonblocking_socket, is_blocking_socket 
-  } mBlockingState;
+  CertVerificationState mCertVerificationState;
   PRUint32 mSecurityState;
   PRInt32 mSubRequestsHighSecurity;
   PRInt32 mSubRequestsLowSecurity;
   PRInt32 mSubRequestsBrokenSecurity;
   PRInt32 mSubRequestsNoSecurity;
   nsString mShortDesc;
 
   PRErrorCode mErrorCode;
-  ErrorMessageType mErrorMessageType;
+  ::mozilla::psm::SSLErrorMessageType mErrorMessageType;
   nsString mErrorMessageCached;
   nsresult formatErrorMessage(::mozilla::MutexAutoLock const & proofOfLock);
 
   bool mDocShellDependentStuffKnown;
   bool mExternalErrorReporting; // DocShellDependent
   bool mForSTARTTLS;
   bool mHandshakePending;
   bool mHasCleartextPhase;
@@ -235,23 +203,19 @@ protected:
   nsXPIDLCString mHostName;
   PRErrorCode mIsCertIssuerBlacklisted;
 
   /* SSL Status */
   nsRefPtr<nsSSLStatus> mSSLStatus;
 
   nsresult ActivateSSL();
 
-  nsSSLSocketThreadData *mThreadData;
-
 private:
   virtual void virtualDestroyNSSReference();
   void destructorSafeDestroyNSSReference();
-
-friend class nsSSLThread;
 };
 
 class nsCStringHashSet;
 
 class nsSSLStatus;
 class nsNSSSocketInfo;
 
 class nsPSMRememberCertErrorsTable
@@ -306,21 +270,16 @@ public:
   static void rememberTolerantSite(PRFileDesc* ssl_layer_fd, nsNSSSocketInfo *socketInfo);
 
   static void addIntolerantSite(const nsCString &str);
   static void removeIntolerantSite(const nsCString &str);
   static bool isKnownAsIntolerantSite(const nsCString &str);
 
   static void setRenegoUnrestrictedSites(const nsCString &str);
   static bool isRenegoUnrestrictedSite(const nsCString &str);
-
-  static PRFileDesc *mSharedPollableEvent;
-  static nsNSSSocketInfo *mSocketOwningPollableEvent;
-  
-  static bool mPollableEventCurrentlySet;
 };
 
 nsresult nsSSLIOLayerNewSocket(PRInt32 family,
                                const char *host,
                                PRInt32 port,
                                const char *proxyHost,
                                PRInt32 proxyPort,
                                PRFileDesc **fd,
deleted file mode 100644
--- a/security/manager/ssl/src/nsSSLThread.cpp
+++ /dev/null
@@ -1,1150 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Red Hat, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Kai Engert <kengert@redhat.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsThreadUtils.h"
-#include "nsSSLThread.h"
-#include "nsNSSIOLayer.h"
-
-#include "ssl.h"
-
-using namespace mozilla;
-
-#ifdef PR_LOGGING
-extern PRLogModuleInfo* gPIPNSSLog;
-#endif
-
-nsSSLThread::nsSSLThread()
-: mBusySocket(nsnull),
-  mSocketScheduledToBeDestroyed(nsnull)
-{
-  NS_ASSERTION(!ssl_thread_singleton, "nsSSLThread is a singleton, caller attempts to create another instance!");
-  
-  ssl_thread_singleton = this;
-}
-
-nsSSLThread::~nsSSLThread()
-{
-  ssl_thread_singleton = nsnull;
-}
-
-PRFileDesc *nsSSLThread::getRealSSLFD(nsNSSSocketInfo *si)
-{
-  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
-    return nsnull;
-
-  MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-  if (si->mThreadData->mReplacedSSLFileDesc)
-  {
-    return si->mThreadData->mReplacedSSLFileDesc;
-  }
-  else
-  {
-    return si->mFd->lower;
-  }
-}
-
-PRStatus nsSSLThread::requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->getsockname(fd, addr);
-}
-
-PRStatus nsSSLThread::requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->getpeername(fd, addr);
-}
-
-PRStatus nsSSLThread::requestGetsocketoption(nsNSSSocketInfo *si, 
-                                             PRSocketOptionData *data)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->getsocketoption(fd, data);
-}
-
-PRStatus nsSSLThread::requestSetsocketoption(nsNSSSocketInfo *si, 
-                                             const PRSocketOptionData *data)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->setsocketoption(fd, data);
-}
-
-PRStatus nsSSLThread::requestConnectcontinue(nsNSSSocketInfo *si, 
-                                             PRInt16 out_flags)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->connectcontinue(fd, out_flags);
-}
-
-PRInt32 nsSSLThread::requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount,
-                                        PRIntn flags, PRIntervalTime timeout)
-{
-  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
-  {
-    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
-    return -1;
-  }
-
-  // Socket is unusable - set error and return -1. See bug #480619.
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown())
-  {
-    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
-    return -1;
-  }
-
-  PRFileDesc *realSSLFD;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (si == ssl_thread_singleton->mBusySocket)
-    {
-      PORT_SetError(PR_WOULD_BLOCK_ERROR);
-      return -1;
-    }
-
-    switch (si->mThreadData->mSSLState)
-    {
-      case nsSSLSocketThreadData::ssl_idle:
-        break;
-    
-      case nsSSLSocketThreadData::ssl_reading_done:
-        {
-          // we have data available that we can return
-
-          // if there was a failure, just return the failure,
-          // but do not yet clear our state, that should happen
-          // in the call to "read".
-
-          if (si->mThreadData->mSSLResultRemainingBytes < 0) {
-            if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
-              PR_SetError(si->mThreadData->mPRErrorCode, 0);
-            }
-
-            return si->mThreadData->mSSLResultRemainingBytes;
-          }
-
-          PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
-
-          memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);
-
-          return return_amount;
-        }
-
-      case nsSSLSocketThreadData::ssl_writing_done:
-      case nsSSLSocketThreadData::ssl_pending_write:
-      case nsSSLSocketThreadData::ssl_pending_read:
-
-      // for safety reasons, also return would_block on any other state,
-      // although this switch statement should be complete and list
-      // the appropriate behaviour for each state.
-      default:
-        {
-          PORT_SetError(PR_WOULD_BLOCK_ERROR);
-          return -1;
-        }
-    }
-
-    if (si->mThreadData->mReplacedSSLFileDesc)
-    {
-      realSSLFD = si->mThreadData->mReplacedSSLFileDesc;
-    }
-    else
-    {
-      realSSLFD = si->mFd->lower;
-    }
-  }
-
-  return realSSLFD->methods->recv(realSSLFD, buf, amount, flags, timeout);
-}
-
-nsresult nsSSLThread::requestActivateSSL(nsNSSSocketInfo *si)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return NS_ERROR_FAILURE;
-
-  if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, true))
-    return NS_ERROR_FAILURE;
-
-  if (SECSuccess != SSL_ResetHandshake(fd, false))
-    return NS_ERROR_FAILURE;
-
-  return NS_OK;
-}
-
-PRInt16 nsSSLThread::requestPoll(nsNSSSocketInfo *si, PRInt16 in_flags, PRInt16 *out_flags)
-{
-  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
-    return 0;
-
-  *out_flags = 0;
-
-  // Socket is unusable - set EXCEPT-flag and return. See bug #480619.
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown())
-  {
-    *out_flags |= PR_POLL_EXCEPT;
-    return in_flags;
-  }
-
-  bool want_sleep_and_wakeup_on_any_socket_activity = false;
-  bool handshake_timeout = false;
-  
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (ssl_thread_singleton->mBusySocket)
-    {
-      // If there is currently any socket busy on the SSL thread,
-      // use our own poll method implementation.
-      
-      switch (si->mThreadData->mSSLState)
-      {
-        case nsSSLSocketThreadData::ssl_writing_done:
-        {
-          if (in_flags & PR_POLL_WRITE)
-          {
-            *out_flags |= PR_POLL_WRITE;
-          }
-
-          return in_flags;
-        }
-        break;
-        
-        case nsSSLSocketThreadData::ssl_reading_done:
-        {
-          if (in_flags & PR_POLL_READ)
-          {
-            *out_flags |= PR_POLL_READ;
-          }
-
-          return in_flags;
-        }
-        break;
-        
-        case nsSSLSocketThreadData::ssl_pending_write:
-        case nsSSLSocketThreadData::ssl_pending_read:
-        {
-          if (si == ssl_thread_singleton->mBusySocket)
-          {
-            if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-            {
-              // The lower layer of the socket is currently the pollable event,
-              // which signals the readable state.
-              
-              return PR_POLL_READ;
-            }
-            else
-            {
-              // Unfortunately we do not have a pollable event
-              // that we could use to wake up the caller, as soon
-              // as the previously requested I/O operation has completed.
-              // Therefore we must use a kind of busy wait,
-              // we want the caller to check again, whenever any
-              // activity is detected on the associated socket.
-              // Unfortunately this could mean, the caller will detect
-              // activity very often, until we are finally done with
-              // the previously requested action and are able to
-              // return the buffered result.
-              // As our real I/O activity is happening on the other thread
-              // let's sleep some cycles, in order to not waste all CPU
-              // resources.
-              // But let's make sure we do not hold our shared mutex
-              // while waiting, so let's leave this block first.
-
-              want_sleep_and_wakeup_on_any_socket_activity = true;
-              break;
-            }
-          }
-          else
-          {
-            // We should never get here, well, at least not with the current
-            // implementation of SSL thread, where we have one worker only.
-            // While another socket is busy, this socket "si" 
-            // can not be marked with pending I/O at the same time.
-            
-            NS_NOTREACHED("Socket not busy on SSL thread marked as pending");
-            return 0;
-          }
-        }
-        break;
-        
-        case nsSSLSocketThreadData::ssl_idle:
-        {
-          if (si->mThreadData->mOneBytePendingFromEarlierWrite)
-          {
-            if (in_flags & PR_POLL_WRITE)
-            {
-              // In this scenario we always want the caller to immediately
-              // try a write again, because it might not wake up otherwise.
-              *out_flags |= PR_POLL_WRITE;
-              return in_flags;
-            }
-          }
-
-          handshake_timeout = si->HandshakeTimeout();
-
-          if (si != ssl_thread_singleton->mBusySocket)
-          {
-            // Some other socket is currently busy on the SSL thread.
-            // It is possible that busy socket is currently blocked (e.g. by UI).
-            // Therefore we should not report "si" as being readable/writeable,
-            // regardless whether it is.
-            // (Because if we did report readable/writeable to the caller,
-            // the caller would repeatedly request us to do I/O, 
-            // although our read/write function would not be able to fulfil
-            // the request, because our single worker is blocked).
-            // To avoid the unnecessary busy loop in that scenario, 
-            // for socket "si" we report "not ready" to the caller.
-            // We do this by faking our caller did not ask for neither
-            // readable nor writeable when querying the lower layer.
-            // (this will leave querying for exceptions enabled)
-            
-            in_flags &= ~(PR_POLL_READ | PR_POLL_WRITE);
-          }
-        }
-        break;
-        
-        default:
-          break;
-      }
-    }
-    else
-    {
-      handshake_timeout = si->HandshakeTimeout();
-    }
-
-    if (handshake_timeout)
-    {
-      NS_ASSERTION(in_flags & PR_POLL_EXCEPT, "nsSSLThread::requestPoll handshake timeout, but caller did not poll for EXCEPT");
-
-      *out_flags |= PR_POLL_EXCEPT;
-      return in_flags;
-    }
-  }
-
-  if (want_sleep_and_wakeup_on_any_socket_activity)
-  {
-    // This is where we wait for any socket activity,
-    // because we do not have a pollable event.
-    // XXX Will this really cause us to wake up
-    //     whatever happens?
-
-    PR_Sleep( PR_MillisecondsToInterval(1) );
-    return PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
-  }
-
-  return si->mFd->lower->methods->poll(si->mFd->lower, in_flags, out_flags);
-}
-
-PRStatus nsSSLThread::requestClose(nsNSSSocketInfo *si)
-{
-  if (!ssl_thread_singleton || !si)
-    return PR_FAILURE;
-
-  bool close_later = false;
-  nsCOMPtr<nsIRequest> requestToCancel;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (ssl_thread_singleton->mBusySocket == si) {
-    
-      // That's tricky, SSL thread is currently busy with this socket,
-      // and might even be blocked on it (UI or OCSP).
-      // We should not close the socket directly, but rather
-      // schedule closing it, at the time the SSL thread is done.
-      // If there is indeed a depending OCSP request pending,
-      // we should cancel it now.
-      
-      if (ssl_thread_singleton->mPendingHTTPRequest)
-      {
-        requestToCancel.swap(ssl_thread_singleton->mPendingHTTPRequest);
-      }
-      
-      close_later = true;
-      ssl_thread_singleton->mSocketScheduledToBeDestroyed = si;
-
-      ssl_thread_singleton->mCond.NotifyAll();
-    }
-  }
-
-  if (requestToCancel)
-  {
-    if (NS_IsMainThread())
-    {
-      requestToCancel->Cancel(NS_ERROR_ABORT);
-    }
-    else
-    {
-      NS_WARNING("Attempt to close SSL socket from a thread that is not the main thread. Can not cancel pending HTTP request from NSS");
-    }
-  
-    requestToCancel = nsnull;
-  }
-  
-  if (!close_later)
-  {
-    return si->CloseSocketAndDestroy();
-  }
-  
-  return PR_SUCCESS;
-}
-
-void nsSSLThread::restoreOriginalSocket_locked(nsNSSSocketInfo *si)
-{
-  if (si->mThreadData->mReplacedSSLFileDesc)
-  {
-    if (nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
-    {
-      nsSSLIOLayerHelpers::mPollableEventCurrentlySet = false;
-      if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-      {
-        PR_WaitForPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
-      }
-    }
-
-    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      // need to restore
-      si->mFd->lower = si->mThreadData->mReplacedSSLFileDesc;
-      si->mThreadData->mReplacedSSLFileDesc = nsnull;
-    }
-
-    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull;
-  }
-}
-
-PRStatus nsSSLThread::getRealFDIfBlockingSocket_locked(nsNSSSocketInfo *si, 
-                                                       PRFileDesc *&out_fd)
-{
-  out_fd = nsnull;
-
-  PRFileDesc *realFD = 
-    (si->mThreadData->mReplacedSSLFileDesc) ?
-      si->mThreadData->mReplacedSSLFileDesc : si->mFd->lower;
-
-  if (si->mBlockingState == nsNSSSocketInfo::blocking_state_unknown)
-  {
-    PRSocketOptionData sod;
-    sod.option = PR_SockOpt_Nonblocking;
-    if (PR_GetSocketOption(realFD, &sod) == PR_FAILURE)
-      return PR_FAILURE;
-
-    si->mBlockingState = sod.value.non_blocking ?
-      nsNSSSocketInfo::is_nonblocking_socket : nsNSSSocketInfo::is_blocking_socket;
-  }
-
-  if (si->mBlockingState == nsNSSSocketInfo::is_blocking_socket)
-  {
-    out_fd = realFD;
-  }
-
-  return PR_SUCCESS;
-}
-
-PRInt32 nsSSLThread::requestRead(nsNSSSocketInfo *si, void *buf, PRInt32 amount, 
-                                 PRIntervalTime timeout)
-{
-  if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
-  {
-    PR_SetError(PR_UNKNOWN_ERROR, 0);
-    return -1;
-  }
-
-  bool this_socket_is_busy = false;
-  bool some_other_socket_is_busy = false;
-  nsSSLSocketThreadData::ssl_state my_ssl_state = nsSSLSocketThreadData::ssl_invalid;
-  PRFileDesc *blockingFD = nsnull;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (ssl_thread_singleton->exitRequested(threadLock)) {
-      PR_SetError(PR_UNKNOWN_ERROR, 0);
-      return -1;
-    }
-
-    if (getRealFDIfBlockingSocket_locked(si, blockingFD) == PR_FAILURE) {
-      return -1;
-    }
-
-    if (!blockingFD)
-    {
-      my_ssl_state = si->mThreadData->mSSLState;
-  
-      if (ssl_thread_singleton->mBusySocket == si)
-      {
-        this_socket_is_busy = true;
-  
-        if (my_ssl_state == nsSSLSocketThreadData::ssl_reading_done)
-        {
-          // we will now care for the data that's ready,
-          // the socket is no longer busy on the ssl thread
-          
-          restoreOriginalSocket_locked(si);
-  
-          ssl_thread_singleton->mBusySocket = nsnull;
-          
-          // We'll handle the results further down,
-          // while not holding the lock.
-        }
-      }
-      else if (ssl_thread_singleton->mBusySocket)
-      {
-        some_other_socket_is_busy = true;
-      }
-  
-      if (!this_socket_is_busy && si->HandshakeTimeout())
-      {
-        restoreOriginalSocket_locked(si);
-        PR_SetError(PR_CONNECT_RESET_ERROR, 0);
-        checkHandshake(-1, true, si->mFd->lower, si);
-        return -1;
-      }
-    }
-    // leave this mutex protected scope before the blockingFD handling
-  }
-
-  if (blockingFD)
-  {
-    // this is an exception, we do not use our SSL thread at all,
-    // just pass the call through to libssl.
-    return blockingFD->methods->recv(blockingFD, buf, amount, 0, timeout);
-  }
-
-  switch (my_ssl_state)
-  {
-    case nsSSLSocketThreadData::ssl_idle:
-      {
-        NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
-        
-        if (some_other_socket_is_busy)
-        {
-          PORT_SetError(PR_WOULD_BLOCK_ERROR);
-          return -1;
-        }
-        
-        // ssl thread is not busy, we'll continue below
-      }
-      break;
-
-    case nsSSLSocketThreadData::ssl_reading_done:
-      // there has been a previous request to read, that is now done!
-      {
-        // failure ?
-        if (si->mThreadData->mSSLResultRemainingBytes < 0) {
-          if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
-            PR_SetError(si->mThreadData->mPRErrorCode, 0);
-            si->mThreadData->mPRErrorCode = PR_SUCCESS;
-          }
-
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-          return si->mThreadData->mSSLResultRemainingBytes;
-        }
-
-        PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
-
-        memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);
-
-        si->mThreadData->mSSLResultRemainingBytes -= return_amount;
-
-        if (!si->mThreadData->mSSLResultRemainingBytes) {
-          si->mThreadData->mSSLRemainingReadResultData = nsnull;
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-        }
-        else {
-          si->mThreadData->mSSLRemainingReadResultData += return_amount;
-        }
-
-        return return_amount;
-      }
-      // we never arrive here, see return statement above
-      break;
-
-
-    // We should not see the following events here,
-    // because we have not yet signaled Necko that we are 
-    // readable/writable again, so if we end up here,
-    // it means that Necko decided to try read/write again,
-    // for whatever reason. No problem, just return would_block,
-    case nsSSLSocketThreadData::ssl_pending_write:
-    case nsSSLSocketThreadData::ssl_pending_read:
-
-    // We should not see this state here, because Necko has previously
-    // requested us to write, Necko is not yet aware that it's done,
-    // (although it meanwhile is), but Necko now tries to read?
-    // If that ever happens, it's confusing, but not a problem,
-    // just let Necko know we can not do that now and return would_block.
-    case nsSSLSocketThreadData::ssl_writing_done:
-
-    // for safety reasons, also return would_block on any other state,
-    // although this switch statement should be complete and list
-    // the appropriate behaviour for each state.
-    default:
-      {
-        PORT_SetError(PR_WOULD_BLOCK_ERROR);
-        return -1;
-      }
-      // we never arrive here, see return statement above
-      break;
-  }
-
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
-    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
-    return -1;
-  }
-
-  if (si->GetErrorCode()) {
-    return PR_FAILURE;
-  }
-
-  // si is idle and good, and no other socket is currently busy,
-  // so it's fine to continue with the request.
-
-  if (!si->mThreadData->ensure_buffer_size(amount))
-  {
-    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
-    return -1;
-  }
-  
-  si->mThreadData->mSSLRequestedTransferAmount = amount;
-  si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_read;
-
-  // Remember we are operating on a layered file descriptor, that consists of
-  // a PSM code layer (nsNSSIOLayer), a NSS code layer (SSL protocol logic), 
-  // and the raw socket at the bottommost layer.
-  //
-  // We don't want to call the SSL layer read/write directly on this thread, 
-  // because it might block, should a callback to UI (for user confirmation)
-  // or Necko (for retrieving OCSP verification data) be necessary.
-  // As Necko is single threaded, it is currently waiting for this 
-  // function to return, and a callback into Necko from NSS couldn't succeed.
-  // 
-  // Therefore we must defer the request to read/write to a separate SSL thread.
-  // We will return WOULD_BLOCK to Necko, and will return the real results
-  // once the I/O operation on the SSL thread is ready.
-  //
-  // The tricky part is to wake up Necko, as soon as the I/O operation 
-  // on the SSL thread is done.
-  //
-  // In order to achieve that, we manipulate the layering of the file 
-  // descriptor. Usually the PSM layer points to the SSL layer as its lower 
-  // layer. We change that to a pollable event file descriptor.
-  //
-  // Once we return from this original read/write function, Necko will 
-  // poll/select on the file descriptor. As result data is not yet ready, we will 
-  // instruct Necko to select on the bottommost file descriptor 
-  // (by using appropriate flags in PSM's layer implementation of the 
-  // poll method), which is the pollable event.
-  //
-  // Once the SSL thread is done with the call to the SSL layer, it will 
-  // "set" the pollable event, causing Necko to wake up on the file descriptor 
-  // and call read/write again. Now that the file descriptor is in the done state, 
-  // we'll arrive in this read/write function again. We'll detect the socket is 
-  // in the done state, and restore the original SSL level file descriptor.
-  // Finally, we return the data obtained on the SSL thread back to our caller.
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
-                   "oops, some other socket still owns our shared pollable event");
-  
-      NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
-  
-      si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
-      si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
-    }
-
-    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
-    ssl_thread_singleton->mBusySocket = si;
-
-    // notify the thread
-    ssl_thread_singleton->mCond.NotifyAll();
-  }
-
-  PORT_SetError(PR_WOULD_BLOCK_ERROR);
-  return -1;
-}
-
-PRInt32 nsSSLThread::requestWrite(nsNSSSocketInfo *si, const void *buf, PRInt32 amount,
-                                  PRIntervalTime timeout)
-{
-  if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
-  {
-    PR_SetError(PR_UNKNOWN_ERROR, 0);
-    return -1;
-  }
-
-  bool this_socket_is_busy = false;
-  bool some_other_socket_is_busy = false;
-  nsSSLSocketThreadData::ssl_state my_ssl_state = nsSSLSocketThreadData::ssl_invalid;
-  PRFileDesc *blockingFD = nsnull;
-  
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-    
-    if (ssl_thread_singleton->exitRequested(threadLock)) {
-      PR_SetError(PR_UNKNOWN_ERROR, 0);
-      return -1;
-    }
-
-    if (getRealFDIfBlockingSocket_locked(si, blockingFD) == PR_FAILURE) {
-      return -1;
-    }
-
-    if (!blockingFD)
-    {
-      my_ssl_state = si->mThreadData->mSSLState;
-  
-      if (ssl_thread_singleton->mBusySocket == si)
-      {
-        this_socket_is_busy = true;
-        
-        if (my_ssl_state == nsSSLSocketThreadData::ssl_writing_done)
-        {
-          // we will now care for the data that's ready,
-          // the socket is no longer busy on the ssl thread
-          
-          restoreOriginalSocket_locked(si);
-  
-          ssl_thread_singleton->mBusySocket = nsnull;
-          
-          // We'll handle the results further down,
-          // while not holding the lock.
-        }
-      }
-      else if (ssl_thread_singleton->mBusySocket)
-      {
-        some_other_socket_is_busy = true;
-      }
-  
-      if (!this_socket_is_busy && si->HandshakeTimeout())
-      {
-        restoreOriginalSocket_locked(si);
-        PR_SetError(PR_CONNECT_RESET_ERROR, 0);
-        checkHandshake(-1, false, si->mFd->lower, si);
-        return -1;
-      }
-    }
-    // leave this mutex protected scope before the blockingFD handling
-  }
-
-  if (blockingFD)
-  {
-    // this is an exception, we do not use our SSL thread at all,
-    // just pass the call through to libssl.
-    return blockingFD->methods->send(blockingFD, buf, amount, 0, timeout);
-  }
-
-  switch (my_ssl_state)
-  {
-    case nsSSLSocketThreadData::ssl_idle:
-      {
-        NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
-        
-        if (some_other_socket_is_busy)
-        {
-          PORT_SetError(PR_WOULD_BLOCK_ERROR);
-          return -1;
-        }
-        
-        // ssl thread is not busy, we'll continue below
-      }
-      break;
-
-    case nsSSLSocketThreadData::ssl_writing_done:
-      // there has been a previous request to write, that is now done!
-      {
-        // failure ?
-        if (si->mThreadData->mSSLResultRemainingBytes < 0) {
-          if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
-            PR_SetError(si->mThreadData->mPRErrorCode, 0);
-            si->mThreadData->mPRErrorCode = PR_SUCCESS;
-          }
-
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-          return si->mThreadData->mSSLResultRemainingBytes;
-        }
-
-        nsSSLIOLayerHelpers::rememberTolerantSite(si->mFd, si);
-
-        PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
-
-        si->mThreadData->mSSLResultRemainingBytes -= return_amount;
-
-        if (!si->mThreadData->mSSLResultRemainingBytes) {
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-        }
-
-        return return_amount;
-      }
-      break;
-      
-    // We should not see the following events here,
-    // because we have not yet signaled Necko that we are 
-    // readable/writable again, so if we end up here,
-    // it means that Necko decided to try read/write again,
-    // for whatever reason. No problem, just return would_block,
-    case nsSSLSocketThreadData::ssl_pending_write:
-    case nsSSLSocketThreadData::ssl_pending_read:
-
-    // We should not see this state here, because Necko has previously
-    // requested us to read, Necko is not yet aware that it's done,
-    // (although it meanwhile is), but Necko now tries to write?
-    // If that ever happens, it's confusing, but not a problem,
-    // just let Necko know we can not do that now and return would_block.
-    case nsSSLSocketThreadData::ssl_reading_done:
-
-    // for safety reasons, also return would_block on any other state,
-    // although this switch statement should be complete and list
-    // the appropriate behaviour for each state.
-    default:
-      {
-        PORT_SetError(PR_WOULD_BLOCK_ERROR);
-        return -1;
-      }
-      // we never arrive here, see return statement above
-      break;
-  }
-
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
-    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
-    return -1;
-  }
-
-  if (si->GetErrorCode()) {
-    return PR_FAILURE;
-  }
-
-  // si is idle and good, and no other socket is currently busy,
-  // so it's fine to continue with the request.
-
-  // However, use special handling for the 
-  //   mOneBytePendingFromEarlierWrite
-  // scenario, where we will not change any of our buffers at this point, 
-  // as we are waiting for completion of the earlier write.
-
-  if (!si->mThreadData->mOneBytePendingFromEarlierWrite)
-  {
-    if (!si->mThreadData->ensure_buffer_size(amount))
-    {
-      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
-      return -1;
-    }
-  
-    memcpy(si->mThreadData->mSSLDataBuffer, buf, amount);
-    si->mThreadData->mSSLRequestedTransferAmount = amount;
-  }
-
-  si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_write;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
-                   "oops, some other socket still owns our shared pollable event");
-  
-      NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
-  
-      si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
-      si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
-    }
-
-    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
-    ssl_thread_singleton->mBusySocket = si;
-
-    ssl_thread_singleton->mCond.NotifyAll();
-  }
-
-  PORT_SetError(PR_WOULD_BLOCK_ERROR);
-  return -1;
-}
-
-void nsSSLThread::Run(void)
-{
-  // Helper variable, we don't want to call destroy 
-  // while holding the mutex.
-  nsNSSSocketInfo *socketToDestroy = nsnull;
-
-  while (true)
-  {
-    if (socketToDestroy)
-    {
-      socketToDestroy->CloseSocketAndDestroy();
-      socketToDestroy = nsnull;
-    }
-
-    // remember whether we'll write or read
-    nsSSLSocketThreadData::ssl_state busy_socket_ssl_state;
-  
-    {
-      // In this scope we need mutex protection,
-      // as we find out what needs to be done.
-      
-      MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-      
-      if (mSocketScheduledToBeDestroyed)
-      {
-        if (mBusySocket == mSocketScheduledToBeDestroyed)
-        {
-          // That's rare, but it happens.
-          // We have received a request to close the socket,
-          // although I/O results have not yet been consumed.
-
-          restoreOriginalSocket_locked(mBusySocket);
-
-          mBusySocket->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-          mBusySocket = nsnull;
-        }
-      
-        socketToDestroy = mSocketScheduledToBeDestroyed;
-        mSocketScheduledToBeDestroyed = nsnull;
-        continue; // go back and finally destroy it, before doing anything else
-      }
-
-      if (exitRequested(threadLock))
-        break;
-
-      bool pending_work = false;
-
-      do
-      {
-        if (mBusySocket
-            &&
-              (mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_read
-              ||
-              mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_write))
-        {
-          pending_work = true;
-        }
-
-        if (!pending_work)
-        {
-          // no work to do ? let's wait a moment
-
-          mCond.Wait();
-        }
-        
-      } while (!pending_work && !exitRequested(threadLock) &&
-               !mSocketScheduledToBeDestroyed);
-      
-      if (mSocketScheduledToBeDestroyed)
-        continue;
-      
-      if (exitRequested(threadLock))
-        break;
-      
-      if (!pending_work)
-        continue;
-      
-      busy_socket_ssl_state = mBusySocket->mThreadData->mSSLState;
-    }
-
-    {
-      // In this scope we need to make sure NSS does not go away
-      // while we are busy.
-      nsNSSShutDownPreventionLock locker;
-
-      // Reference for shorter code and to avoid multiple dereferencing.
-      nsSSLSocketThreadData &bstd = *mBusySocket->mThreadData;
-
-      PRFileDesc *realFileDesc = bstd.mReplacedSSLFileDesc;
-      if (!realFileDesc)
-      {
-        realFileDesc = mBusySocket->mFd->lower;
-      }
-
-      if (nsSSLSocketThreadData::ssl_pending_write == busy_socket_ssl_state)
-      {
-        PRInt32 bytesWritten = 0;
-
-        if (bstd.mOneBytePendingFromEarlierWrite)
-        {
-          // Let's try to flush the final pending byte (that libSSL might already have 
-          // processed). Let's be correct and send the final byte from our buffer.
-          bytesWritten = realFileDesc->methods
-            ->write(realFileDesc, &bstd.mThePendingByte, 1);
-  
-#ifdef DEBUG_SSL_VERBOSE
-          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)realFileDesc, bytesWritten));
-#endif
-          
-          bytesWritten = checkHandshake(bytesWritten, false, realFileDesc, mBusySocket);
-          if (bytesWritten < 0) {
-            // give the error back to caller
-            bstd.mPRErrorCode = PR_GetError();
-          }
-          else if (bytesWritten == 1) {
-            // Cool, all flushed now. We can exit the one-byte-pending mode, 
-            // and report the full amount back to the caller. 
-            bytesWritten = bstd.mOriginalRequestedTransferAmount;
-            bstd.mOriginalRequestedTransferAmount = 0;
-            bstd.mOneBytePendingFromEarlierWrite = false;
-          }
-        }
-        else
-        {
-          // standard code, try to write the buffer we've been given just now 
-          bytesWritten = realFileDesc->methods
-            ->write(realFileDesc, 
-                    bstd.mSSLDataBuffer, 
-                    bstd.mSSLRequestedTransferAmount);
-  
-#ifdef DEBUG_SSL_VERBOSE
-          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes (out of %d)\n", 
-              (void*)realFileDesc, bytesWritten, bstd.mSSLRequestedTransferAmount));
-#endif
-          
-          bytesWritten = checkHandshake(bytesWritten, false, realFileDesc, mBusySocket);
-          if (bytesWritten < 0) {
-            // give the error back to caller
-            bstd.mPRErrorCode = PR_GetError();
-          }
-          else if (bstd.mSSLRequestedTransferAmount > 1 && 
-                   bytesWritten == (bstd.mSSLRequestedTransferAmount - 1)) {
-            // libSSL signaled us a short write.
-            // While libSSL accepted all data, not all bytes were flushed to the OS socket.
-            bstd.mThePendingByte = *(bstd.mSSLDataBuffer + (bstd.mSSLRequestedTransferAmount-1));
-            bytesWritten = -1;
-            bstd.mPRErrorCode = PR_WOULD_BLOCK_ERROR;
-            bstd.mOneBytePendingFromEarlierWrite = true;
-            bstd.mOriginalRequestedTransferAmount = bstd.mSSLRequestedTransferAmount;
-          }
-        }
-
-        bstd.mSSLResultRemainingBytes = bytesWritten;
-        busy_socket_ssl_state = nsSSLSocketThreadData::ssl_writing_done;
-      }
-      else if (nsSSLSocketThreadData::ssl_pending_read == busy_socket_ssl_state)
-      {
-        PRInt32 bytesRead = realFileDesc->methods
-           ->read(realFileDesc, 
-                  bstd.mSSLDataBuffer, 
-                  bstd.mSSLRequestedTransferAmount);
-
-#ifdef DEBUG_SSL_VERBOSE
-        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)realFileDesc, bytesRead));
-#endif
-        bytesRead = checkHandshake(bytesRead, true, realFileDesc, mBusySocket);
-        if (bytesRead < 0) {
-          // give the error back to caller
-          bstd.mPRErrorCode = PR_GetError();
-        }
-
-        bstd.mSSLResultRemainingBytes = bytesRead;
-        bstd.mSSLRemainingReadResultData = bstd.mSSLDataBuffer;
-        busy_socket_ssl_state = nsSSLSocketThreadData::ssl_reading_done;
-      }
-    }
-
-    // avoid setting event repeatedly
-    bool needToSetPollableEvent = false;
-
-    {
-      MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-      
-      mBusySocket->mThreadData->mSSLState = busy_socket_ssl_state;
-      
-      if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
-      {
-        needToSetPollableEvent = true;
-        nsSSLIOLayerHelpers::mPollableEventCurrentlySet = true;
-      }
-    }
-
-    if (needToSetPollableEvent && nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      // Wake up the file descriptor on the Necko thread,
-      // so it can fetch the results from the SSL I/O call 
-      // that we just completed.
-      PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
-
-      // if we don't have a pollable event, we'll have to wake up
-      // the caller by other means.
-    }
-  }
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-    if (mBusySocket)
-    {
-      restoreOriginalSocket_locked(mBusySocket);
-      mBusySocket = nsnull;
-    }
-    if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
-    {
-      nsSSLIOLayerHelpers::mPollableEventCurrentlySet = true;
-      if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-      {
-        PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
-      }
-    }
-    postStoppedEventToMainThread(threadLock);
-  }
-}
-
-bool nsSSLThread::stoppedOrStopping()
-{
-  if (!ssl_thread_singleton)
-    return false;
-
-  return ssl_thread_singleton->exitRequestedNoLock();
-}
-
-nsSSLThread *nsSSLThread::ssl_thread_singleton = nsnull;
deleted file mode 100644
--- a/security/manager/ssl/src/nsSSLThread.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Red Hat, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Kai Engert <kengert@redhat.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _NSSSLTHREAD_H_
-#define _NSSSLTHREAD_H_
-
-#include "nsCOMPtr.h"
-#include "nsIRequest.h"
-#include "nsPSMBackgroundThread.h"
-
-class nsNSSSocketInfo;
-class nsIHttpChannel;
-
-class nsSSLThread : public nsPSMBackgroundThread
-{
-private:
-  // We use mMutex contained in our base class
-  // to protect access to these variables:
-  //   mBusySocket, mSocketScheduledToBeDestroyed
-  // and to nsSSLSocketThreadData::mSSLState
-  // while a socket is the busy socket.
-
-  // We use mCond contained in our base class
-  // to notify the SSL thread that a new SSL I/O 
-  // request has been queued for processing.
-  // It can be found in the mBusySocket variable,
-  // containing all details in its member.
-
-  // A socket that is currently owned by the SSL thread 
-  // and has pending SSL I/O activity or I/O results 
-  // not yet fetched by the original caller.
-  nsNSSSocketInfo *mBusySocket;
-  
-  // A socket that should be closed and destroyed
-  // as soon as possible. The request was initiated by
-  // Necko, but it happened at a time when the SSL
-  // thread had ownership of the socket, so the request
-  // was delayed. It's now the responsibility of the
-  // SSL thread to close and destroy this socket.
-  nsNSSSocketInfo *mSocketScheduledToBeDestroyed;
-
-  // Did we receive a request from NSS to fetch HTTP 
-  // data on behalf of NSS? (Most likely this is a OCSP request)
-  // We track a handle to the HTTP request sent to Necko.
-  // As this HTTP request depends on some original SSL socket,
-  // we can use this handle to cancel the dependent HTTP request,
-  // should we be asked to close the original SSL socket.
-  nsCOMPtr<nsIRequest> mPendingHTTPRequest;
-
-  virtual void Run(void);
-
-  // Called from SSL thread only
-  static PRInt32 checkHandshake(PRInt32 bytesTransfered, 
-                                bool wasReading,
-                                PRFileDesc* fd, 
-                                nsNSSSocketInfo *socketInfo);
-
-  // Function can be called from either Necko or SSL thread
-  // Caller must lock mMutex before this call.
-  static void restoreOriginalSocket_locked(nsNSSSocketInfo *si);
-
-  // Helper for requestSomething functions, 
-  // caled from the Necko thread only.
-  static PRFileDesc *getRealSSLFD(nsNSSSocketInfo *si);
-
-  // Support of blocking sockets is very rudimentary.
-  // We only support it because Mozilla's LDAP code requires blocking I/O.
-  // We do not support switching the blocking mode of a socket.
-  // We require the blocking state has been set prior to the first 
-  // read/write call, and will stay that way for the remainder of the socket's lifetime.
-  // This function must be called while holding the lock.
-  // If the socket is a blocking socket, out_fd will contain the real FD, 
-  // on a non-blocking socket out_fd will be nsnull.
-  // If there is a failure in obtaining the status of the socket,
-  // the function will return PR_FAILURE.
-  static PRStatus getRealFDIfBlockingSocket_locked(nsNSSSocketInfo *si, 
-                                                   PRFileDesc *&out_fd);
-public:
-  nsSSLThread();
-  ~nsSSLThread();
-
-  static nsSSLThread *ssl_thread_singleton;
-
-  // All requestSomething functions are called from 
-  // the Necko thread only.
-
-  static PRInt32 requestRead(nsNSSSocketInfo *si, 
-                             void *buf, 
-                             PRInt32 amount,
-                             PRIntervalTime timeout);
-
-  static PRInt32 requestWrite(nsNSSSocketInfo *si, 
-                              const void *buf, 
-                              PRInt32 amount,
-                              PRIntervalTime timeout);
-
-  static PRInt16 requestPoll(nsNSSSocketInfo *si, 
-                             PRInt16 in_flags, 
-                             PRInt16 *out_flags);
-
-  static PRInt32 requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount,
-                                    PRIntn flags, PRIntervalTime timeout);
-
-  static PRStatus requestClose(nsNSSSocketInfo *si);
-
-  static PRStatus requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr);
-
-  static PRStatus requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr);
-
-  static PRStatus requestGetsocketoption(nsNSSSocketInfo *si, 
-                                         PRSocketOptionData *data);
-
-  static PRStatus requestSetsocketoption(nsNSSSocketInfo *si, 
-                                         const PRSocketOptionData *data);
-
-  static PRStatus requestConnectcontinue(nsNSSSocketInfo *si, 
-                                         PRInt16 out_flags);
-
-  static nsresult requestActivateSSL(nsNSSSocketInfo *si);
-  
-  static bool stoppedOrStopping();
-};
-
-#endif //_NSSSLTHREAD_H_