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 81895 8576199c846c2c9b08a3c1156c564dc48506b280
parent 81894 552810b7513bbe577daa857624ac4b7a2d449ca7
child 81896 7fe6db51869d248d5b3aa4a2c1a62fd62d10648e
push idunknown
push userunknown
push dateunknown
reviewershonzab
bugs674147
milestone11.0a1
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_