Back to index

lightning-sunbird  0.9+nobinonly
nsSSLThread.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is mozilla.org code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Red Hat, Inc.
00018  * Portions created by the Initial Developer are Copyright (C) 2006
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Kai Engert <kengert@redhat.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsIThread.h"
00039 #include "nsSSLThread.h"
00040 #include "nsAutoLock.h"
00041 #include "nsNSSIOLayer.h"
00042 
00043 #include "ssl.h"
00044 
00045 #ifdef PR_LOGGING
00046 extern PRLogModuleInfo* gPIPNSSLog;
00047 #endif
00048 
00049 nsSSLThread::nsSSLThread()
00050 : mBusySocket(nsnull),
00051   mSocketScheduledToBeDestroyed(nsnull),
00052   mPendingHTTPRequest(nsnull)
00053 {
00054   NS_ASSERTION(!ssl_thread_singleton, "nsSSLThread is a singleton, caller attempts to create another instance!");
00055   
00056   ssl_thread_singleton = this;
00057 }
00058 
00059 nsSSLThread::~nsSSLThread()
00060 {
00061   ssl_thread_singleton = nsnull;
00062 }
00063 
00064 PRFileDesc *nsSSLThread::getRealSSLFD(nsNSSSocketInfo *si)
00065 {
00066   if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
00067     return nsnull;
00068 
00069   nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00070 
00071   if (si->mThreadData->mReplacedSSLFileDesc)
00072   {
00073     return si->mThreadData->mReplacedSSLFileDesc;
00074   }
00075   else
00076   {
00077     return si->mFd->lower;
00078   }
00079 }
00080 
00081 PRStatus nsSSLThread::requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr)
00082 {
00083   PRFileDesc *fd = getRealSSLFD(si);
00084   if (!fd)
00085     return PR_FAILURE;
00086 
00087   return fd->methods->getsockname(fd, addr);
00088 }
00089 
00090 PRStatus nsSSLThread::requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr)
00091 {
00092   PRFileDesc *fd = getRealSSLFD(si);
00093   if (!fd)
00094     return PR_FAILURE;
00095 
00096   return fd->methods->getpeername(fd, addr);
00097 }
00098 
00099 PRStatus nsSSLThread::requestGetsocketoption(nsNSSSocketInfo *si, 
00100                                              PRSocketOptionData *data)
00101 {
00102   PRFileDesc *fd = getRealSSLFD(si);
00103   if (!fd)
00104     return PR_FAILURE;
00105 
00106   return fd->methods->getsocketoption(fd, data);
00107 }
00108 
00109 PRStatus nsSSLThread::requestSetsocketoption(nsNSSSocketInfo *si, 
00110                                              const PRSocketOptionData *data)
00111 {
00112   PRFileDesc *fd = getRealSSLFD(si);
00113   if (!fd)
00114     return PR_FAILURE;
00115 
00116   return fd->methods->setsocketoption(fd, data);
00117 }
00118 
00119 PRStatus nsSSLThread::requestConnectcontinue(nsNSSSocketInfo *si, 
00120                                              PRInt16 out_flags)
00121 {
00122   PRFileDesc *fd = getRealSSLFD(si);
00123   if (!fd)
00124     return PR_FAILURE;
00125 
00126   return fd->methods->connectcontinue(fd, out_flags);
00127 }
00128 
00129 PRInt32 nsSSLThread::requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount,
00130                                         PRIntn flags, PRIntervalTime timeout)
00131 {
00132   if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
00133   {
00134     PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
00135     return -1;
00136   }
00137 
00138   PRFileDesc *realSSLFD;
00139 
00140   {
00141     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00142 
00143     if (si == ssl_thread_singleton->mBusySocket)
00144     {
00145       PORT_SetError(PR_WOULD_BLOCK_ERROR);
00146       return -1;
00147     }
00148 
00149     switch (si->mThreadData->mSSLState)
00150     {
00151       case nsSSLSocketThreadData::ssl_idle:
00152         break;
00153     
00154       case nsSSLSocketThreadData::ssl_reading_done:
00155         {
00156           // we have data available that we can return
00157 
00158           // if there was a failure, just return the failure,
00159           // but do not yet clear our state, that should happen
00160           // in the call to "read".
00161 
00162           if (si->mThreadData->mSSLResultRemainingBytes < 0) {
00163             if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
00164               PR_SetError(si->mThreadData->mPRErrorCode, 0);
00165             }
00166 
00167             return si->mThreadData->mSSLResultRemainingBytes;
00168           }
00169 
00170           PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
00171 
00172           memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);
00173 
00174           return return_amount;
00175         }
00176 
00177       case nsSSLSocketThreadData::ssl_writing_done:
00178       case nsSSLSocketThreadData::ssl_pending_write:
00179       case nsSSLSocketThreadData::ssl_pending_read:
00180 
00181       // for safety reasons, also return would_block on any other state,
00182       // although this switch statement should be complete and list
00183       // the appropriate behaviour for each state.
00184       default:
00185         {
00186           PORT_SetError(PR_WOULD_BLOCK_ERROR);
00187           return -1;
00188         }
00189     }
00190 
00191     if (si->mThreadData->mReplacedSSLFileDesc)
00192     {
00193       realSSLFD = si->mThreadData->mReplacedSSLFileDesc;
00194     }
00195     else
00196     {
00197       realSSLFD = si->mFd->lower;
00198     }
00199   }
00200 
00201   return realSSLFD->methods->recv(realSSLFD, buf, amount, flags, timeout);
00202 }
00203 
00204 nsresult nsSSLThread::requestActivateSSL(nsNSSSocketInfo *si)
00205 {
00206   PRFileDesc *fd = getRealSSLFD(si);
00207   if (!fd)
00208     return NS_ERROR_FAILURE;
00209 
00210   if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, PR_TRUE))
00211     return NS_ERROR_FAILURE;
00212 
00213   if (SECSuccess != SSL_ResetHandshake(fd, PR_FALSE))
00214     return NS_ERROR_FAILURE;
00215 
00216   return NS_OK;
00217 }
00218 
00219 PRInt16 nsSSLThread::requestPoll(nsNSSSocketInfo *si, PRInt16 in_flags, PRInt16 *out_flags)
00220 {
00221   if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
00222     return 0;
00223 
00224   *out_flags = 0;
00225 
00226   PRBool want_sleep_and_wakeup_on_any_socket_activity = PR_FALSE;
00227   PRBool handshake_timeout = PR_FALSE;
00228   
00229   {
00230     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00231 
00232     if (ssl_thread_singleton->mBusySocket)
00233     {
00234       // If there is currently any socket busy on the SSL thread,
00235       // use our own poll method implementation.
00236       
00237       switch (si->mThreadData->mSSLState)
00238       {
00239         case nsSSLSocketThreadData::ssl_writing_done:
00240         {
00241           if (in_flags & PR_POLL_WRITE)
00242           {
00243             *out_flags |= PR_POLL_WRITE;
00244           }
00245 
00246           return in_flags;
00247         }
00248         break;
00249         
00250         case nsSSLSocketThreadData::ssl_reading_done:
00251         {
00252           if (in_flags & PR_POLL_READ)
00253           {
00254             *out_flags |= PR_POLL_READ;
00255           }
00256 
00257           return in_flags;
00258         }
00259         break;
00260         
00261         case nsSSLSocketThreadData::ssl_pending_write:
00262         case nsSSLSocketThreadData::ssl_pending_read:
00263         {
00264           if (si == ssl_thread_singleton->mBusySocket)
00265           {
00266             if (nsSSLIOLayerHelpers::mSharedPollableEvent)
00267             {
00268               // The lower layer of the socket is currently the pollable event,
00269               // which signals the readable state.
00270               
00271               return PR_POLL_READ;
00272             }
00273             else
00274             {
00275               // Unfortunately we do not have a pollable event
00276               // that we could use to wake up the caller, as soon
00277               // as the previously requested I/O operation has completed.
00278               // Therefore we must use a kind of busy wait,
00279               // we want the caller to check again, whenever any
00280               // activity is detected on the associated socket.
00281               // Unfortunately this could mean, the caller will detect
00282               // activity very often, until we are finally done with
00283               // the previously requested action and are able to
00284               // return the buffered result.
00285               // As our real I/O activity is happening on the other thread
00286               // let's sleep some cycles, in order to not waste all CPU
00287               // resources.
00288               // But let's make sure we do not hold our shared mutex
00289               // while waiting, so let's leave this block first.
00290 
00291               want_sleep_and_wakeup_on_any_socket_activity = PR_TRUE;
00292               break;
00293             }
00294           }
00295           else
00296           {
00297             // We should never get here, well, at least not with the current
00298             // implementation of SSL thread, where we have one worker only.
00299             // While another socket is busy, this socket "si" 
00300             // can not be marked with pending I/O at the same time.
00301             
00302             NS_NOTREACHED("Socket not busy on SSL thread marked as pending");
00303             return 0;
00304           }
00305         }
00306         break;
00307         
00308         case nsSSLSocketThreadData::ssl_idle:
00309         {
00310           if (si->mThreadData->mOneBytePendingFromEarlierWrite)
00311           {
00312             if (in_flags & PR_POLL_WRITE)
00313             {
00314               // In this scenario we always want the caller to immediately
00315               // try a write again, because it might not wake up otherwise.
00316               *out_flags |= PR_POLL_WRITE;
00317               return in_flags;
00318             }
00319           }
00320 
00321           handshake_timeout = si->HandshakeTimeout();
00322 
00323           if (si != ssl_thread_singleton->mBusySocket)
00324           {
00325             // Some other socket is currently busy on the SSL thread.
00326             // It is possible that busy socket is currently blocked (e.g. by UI).
00327             // Therefore we should not report "si" as being readable/writeable,
00328             // regardless whether it is.
00329             // (Because if we did report readable/writeable to the caller,
00330             // the caller would repeatedly request us to do I/O, 
00331             // although our read/write function would not be able to fulfil
00332             // the request, because our single worker is blocked).
00333             // To avoid the unnecessary busy loop in that scenario, 
00334             // for socket "si" we report "not ready" to the caller.
00335             // We do this by faking our caller did not ask for neither
00336             // readable nor writeable when querying the lower layer.
00337             // (this will leave querying for exceptions enabled)
00338             
00339             in_flags &= ~(PR_POLL_READ | PR_POLL_WRITE);
00340           }
00341         }
00342         break;
00343         
00344         default:
00345           break;
00346       }
00347     }
00348     else
00349     {
00350       handshake_timeout = si->HandshakeTimeout();
00351     }
00352 
00353     if (handshake_timeout)
00354     {
00355       NS_ASSERTION(in_flags & PR_POLL_EXCEPT, "nsSSLThread::requestPoll handshake timeout, but caller did not poll for EXCEPT");
00356 
00357       *out_flags |= PR_POLL_EXCEPT;
00358       return in_flags;
00359     }
00360   }
00361 
00362   if (want_sleep_and_wakeup_on_any_socket_activity)
00363   {
00364     // This is where we wait for any socket activity,
00365     // because we do not have a pollable event.
00366     // XXX Will this really cause us to wake up
00367     //     whatever happens?
00368 
00369     PR_Sleep( PR_MillisecondsToInterval(1) );
00370     return PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
00371   }
00372 
00373   return si->mFd->lower->methods->poll(si->mFd->lower, in_flags, out_flags);
00374 }
00375 
00376 PRStatus nsSSLThread::requestClose(nsNSSSocketInfo *si)
00377 {
00378   if (!ssl_thread_singleton || !si)
00379     return PR_FAILURE;
00380 
00381   PRBool close_later = PR_FALSE;
00382   nsIRequest* requestToCancel = nsnull;
00383 
00384   {
00385     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00386 
00387     if (ssl_thread_singleton->mBusySocket == si) {
00388     
00389       // That's tricky, SSL thread is currently busy with this socket,
00390       // and might even be blocked on it (UI or OCSP).
00391       // We should not close the socket directly, but rather
00392       // schedule closing it, at the time the SSL thread is done.
00393       // If there is indeed a depending OCSP request pending,
00394       // we should cancel it now.
00395       
00396       if (ssl_thread_singleton->mPendingHTTPRequest)
00397       {
00398         requestToCancel = ssl_thread_singleton->mPendingHTTPRequest;
00399         ssl_thread_singleton->mPendingHTTPRequest = nsnull;
00400       }
00401       
00402       close_later = PR_TRUE;
00403       ssl_thread_singleton->mSocketScheduledToBeDestroyed = si;
00404 
00405       PR_NotifyAllCondVar(ssl_thread_singleton->mCond);
00406     }
00407   }
00408 
00409   if (requestToCancel)
00410   {
00411     if (nsIThread::IsMainThread())
00412     {
00413       requestToCancel->Cancel(NS_ERROR_ABORT);
00414     }
00415     else
00416     {
00417       NS_WARNING("Attempt to close SSL socket from a thread that is not the main thread. Can not cancel pending HTTP request from NSS");
00418     }
00419   
00420     NS_RELEASE(requestToCancel);
00421   }
00422   
00423   if (!close_later)
00424   {
00425     return si->CloseSocketAndDestroy();
00426   }
00427   
00428   return PR_SUCCESS;
00429 }
00430 
00431 void nsSSLThread::restoreOriginalSocket_locked(nsNSSSocketInfo *si)
00432 {
00433   if (si->mThreadData->mReplacedSSLFileDesc)
00434   {
00435     if (nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
00436     {
00437       nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_FALSE;
00438       if (nsSSLIOLayerHelpers::mSharedPollableEvent)
00439       {
00440         PR_WaitForPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
00441       }
00442     }
00443 
00444     if (nsSSLIOLayerHelpers::mSharedPollableEvent)
00445     {
00446       // need to restore
00447       si->mFd->lower = si->mThreadData->mReplacedSSLFileDesc;
00448       si->mThreadData->mReplacedSSLFileDesc = nsnull;
00449     }
00450 
00451     nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull;
00452   }
00453 }
00454 
00455 PRInt32 nsSSLThread::requestRead(nsNSSSocketInfo *si, void *buf, PRInt32 amount)
00456 {
00457   if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
00458   {
00459     PR_SetError(PR_UNKNOWN_ERROR, 0);
00460     return -1;
00461   }
00462 
00463   PRBool this_socket_is_busy = PR_FALSE;
00464   PRBool some_other_socket_is_busy = PR_FALSE;
00465   nsSSLSocketThreadData::ssl_state my_ssl_state;
00466 
00467   {
00468     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00469 
00470     if (ssl_thread_singleton->mExitRequested) {
00471       PR_SetError(PR_UNKNOWN_ERROR, 0);
00472       return -1;
00473     }
00474 
00475     my_ssl_state = si->mThreadData->mSSLState;
00476 
00477     if (ssl_thread_singleton->mBusySocket == si)
00478     {
00479       this_socket_is_busy = PR_TRUE;
00480 
00481       if (my_ssl_state == nsSSLSocketThreadData::ssl_reading_done)
00482       {
00483         // we will now care for the data that's ready,
00484         // the socket is no longer busy on the ssl thread
00485         
00486         restoreOriginalSocket_locked(si);
00487 
00488         ssl_thread_singleton->mBusySocket = nsnull;
00489         
00490         // We'll handle the results further down,
00491         // while not holding the lock.
00492       }
00493     }
00494     else if (ssl_thread_singleton->mBusySocket)
00495     {
00496       some_other_socket_is_busy = PR_TRUE;
00497     }
00498 
00499     if (!this_socket_is_busy && si->HandshakeTimeout())
00500     {
00501       restoreOriginalSocket_locked(si);
00502       PR_SetError(PR_CONNECT_RESET_ERROR, 0);
00503       checkHandshake(-1, PR_TRUE, si->mFd->lower, si);
00504       return -1;
00505     }
00506   }
00507 
00508   switch (my_ssl_state)
00509   {
00510     case nsSSLSocketThreadData::ssl_idle:
00511       {
00512         NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
00513         
00514         if (some_other_socket_is_busy)
00515         {
00516           PORT_SetError(PR_WOULD_BLOCK_ERROR);
00517           return -1;
00518         }
00519         
00520         // ssl thread is not busy, we'll continue below
00521       }
00522       break;
00523 
00524     case nsSSLSocketThreadData::ssl_reading_done:
00525       // there has been a previous request to read, that is now done!
00526       {
00527         // failure ?
00528         if (si->mThreadData->mSSLResultRemainingBytes < 0) {
00529           if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
00530             PR_SetError(si->mThreadData->mPRErrorCode, 0);
00531             si->mThreadData->mPRErrorCode = PR_SUCCESS;
00532           }
00533 
00534           si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
00535           return si->mThreadData->mSSLResultRemainingBytes;
00536         }
00537 
00538         PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
00539 
00540         memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);
00541 
00542         si->mThreadData->mSSLResultRemainingBytes -= return_amount;
00543 
00544         if (!si->mThreadData->mSSLResultRemainingBytes) {
00545           si->mThreadData->mSSLRemainingReadResultData = nsnull;
00546           si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
00547         }
00548         else {
00549           si->mThreadData->mSSLRemainingReadResultData += return_amount;
00550         }
00551 
00552         return return_amount;
00553       }
00554       // we never arrive here, see return statement above
00555       break;
00556 
00557 
00558     // We should not see the following events here,
00559     // because we have not yet signaled Necko that we are 
00560     // readable/writable again, so if we end up here,
00561     // it means that Necko decided to try read/write again,
00562     // for whatever reason. No problem, just return would_block,
00563     case nsSSLSocketThreadData::ssl_pending_write:
00564     case nsSSLSocketThreadData::ssl_pending_read:
00565 
00566     // We should not see this state here, because Necko has previously
00567     // requested us to write, Necko is not yet aware that it's done,
00568     // (although it meanwhile is), but Necko now tries to read?
00569     // If that ever happens, it's confusing, but not a problem,
00570     // just let Necko know we can not do that now and return would_block.
00571     case nsSSLSocketThreadData::ssl_writing_done:
00572 
00573     // for safety reasons, also return would_block on any other state,
00574     // although this switch statement should be complete and list
00575     // the appropriate behaviour for each state.
00576     default:
00577       {
00578         PORT_SetError(PR_WOULD_BLOCK_ERROR);
00579         return -1;
00580       }
00581       // we never arrive here, see return statement above
00582       break;
00583   }
00584 
00585   if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
00586     PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
00587     return -1;
00588   }
00589 
00590   if (si->GetCanceled()) {
00591     return PR_FAILURE;
00592   }
00593 
00594   // si is idle and good, and no other socket is currently busy,
00595   // so it's fine to continue with the request.
00596 
00597   if (!si->mThreadData->ensure_buffer_size(amount))
00598   {
00599     PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00600     return -1;
00601   }
00602   
00603   si->mThreadData->mSSLRequestedTransferAmount = amount;
00604   si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_read;
00605 
00606   // Remember we are operating on a layered file descriptor, that consists of
00607   // a PSM code layer (nsNSSIOLayer), a NSS code layer (SSL protocol logic), 
00608   // and the raw socket at the bottommost layer.
00609   //
00610   // We don't want to call the SSL layer read/write directly on this thread, 
00611   // because it might block, should a callback to UI (for user confirmation)
00612   // or Necko (for retrieving OCSP verification data) be necessary.
00613   // As Necko is single threaded, it is currently waiting for this 
00614   // function to return, and a callback into Necko from NSS couldn't succeed.
00615   // 
00616   // Therefore we must defer the request to read/write to a separate SSL thread.
00617   // We will return WOULD_BLOCK to Necko, and will return the real results
00618   // once the I/O operation on the SSL thread is ready.
00619   //
00620   // The tricky part is to wake up Necko, as soon as the I/O operation 
00621   // on the SSL thread is done.
00622   //
00623   // In order to achieve that, we manipulate the layering of the file 
00624   // descriptor. Usually the PSM layer points to the SSL layer as its lower 
00625   // layer. We change that to a pollable event file descriptor.
00626   //
00627   // Once we return from this original read/write function, Necko will 
00628   // poll/select on the file descriptor. As result data is not yet ready, we will 
00629   // instruct Necko to select on the bottommost file descriptor 
00630   // (by using appropriate flags in PSM's layer implementation of the 
00631   // poll method), which is the pollable event.
00632   //
00633   // Once the SSL thread is done with the call to the SSL layer, it will 
00634   // "set" the pollable event, causing Necko to wake up on the file descriptor 
00635   // and call read/write again. Now that the file descriptor is in the done state, 
00636   // we'll arrive in this read/write function again. We'll detect the socket is 
00637   // in the done state, and restore the original SSL level file descriptor.
00638   // Finally, we return the data obtained on the SSL thread back to our caller.
00639 
00640   {
00641     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00642 
00643     if (nsSSLIOLayerHelpers::mSharedPollableEvent)
00644     {
00645       NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
00646                    "oops, some other socket still owns our shared pollable event");
00647   
00648       NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
00649   
00650       si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
00651       si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
00652     }
00653 
00654     nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
00655     ssl_thread_singleton->mBusySocket = si;
00656 
00657     // notify the thread
00658     PR_NotifyAllCondVar(ssl_thread_singleton->mCond);
00659   }
00660 
00661   PORT_SetError(PR_WOULD_BLOCK_ERROR);
00662   return -1;
00663 }
00664 
00665 PRInt32 nsSSLThread::requestWrite(nsNSSSocketInfo *si, const void *buf, PRInt32 amount)
00666 {
00667   if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
00668   {
00669     PR_SetError(PR_UNKNOWN_ERROR, 0);
00670     return -1;
00671   }
00672 
00673   PRBool this_socket_is_busy = PR_FALSE;
00674   PRBool some_other_socket_is_busy = PR_FALSE;
00675   nsSSLSocketThreadData::ssl_state my_ssl_state;
00676   
00677   {
00678     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00679     
00680     if (ssl_thread_singleton->mExitRequested) {
00681       PR_SetError(PR_UNKNOWN_ERROR, 0);
00682       return -1;
00683     }
00684 
00685     my_ssl_state = si->mThreadData->mSSLState;
00686 
00687     if (ssl_thread_singleton->mBusySocket == si)
00688     {
00689       this_socket_is_busy = PR_TRUE;
00690       
00691       if (my_ssl_state == nsSSLSocketThreadData::ssl_writing_done)
00692       {
00693         // we will now care for the data that's ready,
00694         // the socket is no longer busy on the ssl thread
00695         
00696         restoreOriginalSocket_locked(si);
00697 
00698         ssl_thread_singleton->mBusySocket = nsnull;
00699         
00700         // We'll handle the results further down,
00701         // while not holding the lock.
00702       }
00703     }
00704     else if (ssl_thread_singleton->mBusySocket)
00705     {
00706       some_other_socket_is_busy = PR_TRUE;
00707     }
00708 
00709     if (!this_socket_is_busy && si->HandshakeTimeout())
00710     {
00711       restoreOriginalSocket_locked(si);
00712       PR_SetError(PR_CONNECT_RESET_ERROR, 0);
00713       checkHandshake(-1, PR_FALSE, si->mFd->lower, si);
00714       return -1;
00715     }
00716   }
00717 
00718   switch (my_ssl_state)
00719   {
00720     case nsSSLSocketThreadData::ssl_idle:
00721       {
00722         NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
00723         
00724         if (some_other_socket_is_busy)
00725         {
00726           PORT_SetError(PR_WOULD_BLOCK_ERROR);
00727           return -1;
00728         }
00729         
00730         // ssl thread is not busy, we'll continue below
00731       }
00732       break;
00733 
00734     case nsSSLSocketThreadData::ssl_writing_done:
00735       // there has been a previous request to write, that is now done!
00736       {
00737         // failure ?
00738         if (si->mThreadData->mSSLResultRemainingBytes < 0) {
00739           if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
00740             PR_SetError(si->mThreadData->mPRErrorCode, 0);
00741             si->mThreadData->mPRErrorCode = PR_SUCCESS;
00742           }
00743 
00744           si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
00745           return si->mThreadData->mSSLResultRemainingBytes;
00746         }
00747 
00748         PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
00749 
00750         si->mThreadData->mSSLResultRemainingBytes -= return_amount;
00751 
00752         if (!si->mThreadData->mSSLResultRemainingBytes) {
00753           si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
00754         }
00755 
00756         return return_amount;
00757       }
00758       break;
00759       
00760     // We should not see the following events here,
00761     // because we have not yet signaled Necko that we are 
00762     // readable/writable again, so if we end up here,
00763     // it means that Necko decided to try read/write again,
00764     // for whatever reason. No problem, just return would_block,
00765     case nsSSLSocketThreadData::ssl_pending_write:
00766     case nsSSLSocketThreadData::ssl_pending_read:
00767 
00768     // We should not see this state here, because Necko has previously
00769     // requested us to read, Necko is not yet aware that it's done,
00770     // (although it meanwhile is), but Necko now tries to write?
00771     // If that ever happens, it's confusing, but not a problem,
00772     // just let Necko know we can not do that now and return would_block.
00773     case nsSSLSocketThreadData::ssl_reading_done:
00774 
00775     // for safety reasons, also return would_block on any other state,
00776     // although this switch statement should be complete and list
00777     // the appropriate behaviour for each state.
00778     default:
00779       {
00780         PORT_SetError(PR_WOULD_BLOCK_ERROR);
00781         return -1;
00782       }
00783       // we never arrive here, see return statement above
00784       break;
00785   }
00786 
00787   if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
00788     PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
00789     return -1;
00790   }
00791 
00792   if (si->GetCanceled()) {
00793     return PR_FAILURE;
00794   }
00795 
00796   // si is idle and good, and no other socket is currently busy,
00797   // so it's fine to continue with the request.
00798 
00799   // However, use special handling for the 
00800   //   mOneBytePendingFromEarlierWrite
00801   // scenario, where we will not change any of our buffers at this point, 
00802   // as we are waiting for completion of the earlier write.
00803 
00804   if (!si->mThreadData->mOneBytePendingFromEarlierWrite)
00805   {
00806     if (!si->mThreadData->ensure_buffer_size(amount))
00807     {
00808       PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00809       return -1;
00810     }
00811   
00812     memcpy(si->mThreadData->mSSLDataBuffer, buf, amount);
00813     si->mThreadData->mSSLRequestedTransferAmount = amount;
00814   }
00815 
00816   si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_write;
00817 
00818   {
00819     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00820 
00821     if (nsSSLIOLayerHelpers::mSharedPollableEvent)
00822     {
00823       NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
00824                    "oops, some other socket still owns our shared pollable event");
00825   
00826       NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
00827   
00828       si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
00829       si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
00830     }
00831 
00832     nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
00833     ssl_thread_singleton->mBusySocket = si;
00834 
00835     PR_NotifyAllCondVar(ssl_thread_singleton->mCond);
00836   }
00837 
00838   PORT_SetError(PR_WOULD_BLOCK_ERROR);
00839   return -1;
00840 }
00841 
00842 void nsSSLThread::Run(void)
00843 {
00844   // Helper variable, we don't want to call destroy 
00845   // while holding the mutex.
00846   nsNSSSocketInfo *socketToDestroy = nsnull;
00847 
00848   while (PR_TRUE)
00849   {
00850     if (socketToDestroy)
00851     {
00852       socketToDestroy->CloseSocketAndDestroy();
00853       socketToDestroy = nsnull;
00854     }
00855 
00856     // remember whether we'll write or read
00857     nsSSLSocketThreadData::ssl_state busy_socket_ssl_state;
00858   
00859     {
00860       // In this scope we need mutex protection,
00861       // as we find out what needs to be done.
00862       
00863       nsAutoLock threadLock(ssl_thread_singleton->mMutex);
00864       
00865       if (mSocketScheduledToBeDestroyed)
00866       {
00867         if (mBusySocket == mSocketScheduledToBeDestroyed)
00868         {
00869           // That's rare, but it happens.
00870           // We have received a request to close the socket,
00871           // although I/O results have not yet been consumed.
00872 
00873           restoreOriginalSocket_locked(mBusySocket);
00874 
00875           mBusySocket->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
00876           mBusySocket = nsnull;
00877         }
00878       
00879         socketToDestroy = mSocketScheduledToBeDestroyed;
00880         mSocketScheduledToBeDestroyed = nsnull;
00881         continue; // go back and finally destroy it, before doing anything else
00882       }
00883 
00884       if (mExitRequested)
00885         break;
00886 
00887       PRBool pending_work = PR_FALSE;
00888 
00889       do
00890       {
00891         if (mBusySocket
00892             &&
00893               (mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_read
00894               ||
00895               mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_write))
00896         {
00897           pending_work = PR_TRUE;
00898         }
00899 
00900         if (!pending_work)
00901         {
00902           // no work to do ? let's wait a moment
00903 
00904           PR_WaitCondVar(mCond, PR_INTERVAL_NO_TIMEOUT);
00905         }
00906         
00907       } while (!pending_work && !mExitRequested && !mSocketScheduledToBeDestroyed);
00908       
00909       if (mSocketScheduledToBeDestroyed)
00910         continue;
00911       
00912       if (mExitRequested)
00913         break;
00914       
00915       if (!pending_work)
00916         continue;
00917       
00918       busy_socket_ssl_state = mBusySocket->mThreadData->mSSLState;
00919     }
00920 
00921     {
00922       // In this scope we need to make sure NSS does not go away
00923       // while we are busy.
00924       nsNSSShutDownPreventionLock locker;
00925 
00926       // Reference for shorter code and to avoid multiple dereferencing.
00927       nsSSLSocketThreadData &bstd = *mBusySocket->mThreadData;
00928 
00929       PRFileDesc *realFileDesc = bstd.mReplacedSSLFileDesc;
00930       if (!realFileDesc)
00931       {
00932         realFileDesc = mBusySocket->mFd->lower;
00933       }
00934 
00935       if (nsSSLSocketThreadData::ssl_pending_write == busy_socket_ssl_state)
00936       {
00937         PRInt32 bytesWritten = 0;
00938 
00939         if (bstd.mOneBytePendingFromEarlierWrite)
00940         {
00941           // Let's try to flush the final pending byte (that libSSL might already have 
00942           // processed). Let's be correct and send the final byte from our buffer.
00943           bytesWritten = realFileDesc->methods
00944             ->write(realFileDesc, &bstd.mThePendingByte, 1);
00945   
00946 #ifdef DEBUG_SSL_VERBOSE
00947           PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)realFileDesc, bytesWritten));
00948 #endif
00949           
00950           bytesWritten = checkHandshake(bytesWritten, PR_FALSE, realFileDesc, mBusySocket);
00951           if (bytesWritten < 0) {
00952             // give the error back to caller
00953             bstd.mPRErrorCode = PR_GetError();
00954           }
00955           else if (bytesWritten == 1) {
00956             // Cool, all flushed now. We can exit the one-byte-pending mode, 
00957             // and report the full amount back to the caller. 
00958             bytesWritten = bstd.mOriginalRequestedTransferAmount;
00959             bstd.mOriginalRequestedTransferAmount = 0;
00960             bstd.mOneBytePendingFromEarlierWrite = PR_FALSE;
00961           }
00962         }
00963         else
00964         {
00965           // standard code, try to write the buffer we've been given just now 
00966           bytesWritten = realFileDesc->methods
00967             ->write(realFileDesc, 
00968                     bstd.mSSLDataBuffer, 
00969                     bstd.mSSLRequestedTransferAmount);
00970   
00971 #ifdef DEBUG_SSL_VERBOSE
00972           PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes (out of %d)\n", 
00973               (void*)realFileDesc, bytesWritten, bstd.mSSLRequestedTransferAmount));
00974 #endif
00975           
00976           bytesWritten = checkHandshake(bytesWritten, PR_FALSE, realFileDesc, mBusySocket);
00977           if (bytesWritten < 0) {
00978             // give the error back to caller
00979             bstd.mPRErrorCode = PR_GetError();
00980           }
00981           else if (bstd.mSSLRequestedTransferAmount > 1 && 
00982                    bytesWritten == (bstd.mSSLRequestedTransferAmount - 1)) {
00983             // libSSL signaled us a short write.
00984             // While libSSL accepted all data, not all bytes were flushed to the OS socket.
00985             bstd.mThePendingByte = *(bstd.mSSLDataBuffer + (bstd.mSSLRequestedTransferAmount-1));
00986             bytesWritten = -1;
00987             bstd.mPRErrorCode = PR_WOULD_BLOCK_ERROR;
00988             bstd.mOneBytePendingFromEarlierWrite = PR_TRUE;
00989             bstd.mOriginalRequestedTransferAmount = bstd.mSSLRequestedTransferAmount;
00990           }
00991         }
00992 
00993         bstd.mSSLResultRemainingBytes = bytesWritten;
00994         busy_socket_ssl_state = nsSSLSocketThreadData::ssl_writing_done;
00995       }
00996       else if (nsSSLSocketThreadData::ssl_pending_read == busy_socket_ssl_state)
00997       {
00998         PRInt32 bytesRead = realFileDesc->methods
00999            ->read(realFileDesc, 
01000                   bstd.mSSLDataBuffer, 
01001                   bstd.mSSLRequestedTransferAmount);
01002 
01003 #ifdef DEBUG_SSL_VERBOSE
01004         PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)realFileDesc, bytesRead));
01005 #endif
01006         bytesRead = checkHandshake(bytesRead, PR_TRUE, realFileDesc, mBusySocket);
01007         if (bytesRead < 0) {
01008           // give the error back to caller
01009           bstd.mPRErrorCode = PR_GetError();
01010         }
01011 
01012         bstd.mSSLResultRemainingBytes = bytesRead;
01013         bstd.mSSLRemainingReadResultData = bstd.mSSLDataBuffer;
01014         busy_socket_ssl_state = nsSSLSocketThreadData::ssl_reading_done;
01015       }
01016     }
01017 
01018     // avoid setting event repeatedly
01019     PRBool needToSetPollableEvent = PR_FALSE;
01020 
01021     {
01022       nsAutoLock threadLock(ssl_thread_singleton->mMutex);
01023       
01024       mBusySocket->mThreadData->mSSLState = busy_socket_ssl_state;
01025       
01026       if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
01027       {
01028         needToSetPollableEvent = PR_TRUE;
01029         nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_TRUE;
01030       }
01031     }
01032 
01033     if (needToSetPollableEvent && nsSSLIOLayerHelpers::mSharedPollableEvent)
01034     {
01035       // Wake up the file descriptor on the Necko thread,
01036       // so it can fetch the results from the SSL I/O call 
01037       // that we just completed.
01038       PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
01039 
01040       // if we don't have a pollable event, we'll have to wake up
01041       // the caller by other means.
01042     }
01043   }
01044 
01045   {
01046     nsAutoLock threadLock(ssl_thread_singleton->mMutex);
01047     if (mBusySocket)
01048     {
01049       restoreOriginalSocket_locked(mBusySocket);
01050       mBusySocket = nsnull;
01051     }
01052     if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
01053     {
01054       nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_TRUE;
01055       if (nsSSLIOLayerHelpers::mSharedPollableEvent)
01056       {
01057         PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
01058       }
01059     }
01060   }
01061 }
01062 
01063 void nsSSLThread::rememberPendingHTTPRequest(nsIRequest *aRequest)
01064 {
01065   if (!ssl_thread_singleton)
01066     return;
01067 
01068   nsAutoLock threadLock(ssl_thread_singleton->mMutex);
01069 
01070   NS_IF_ADDREF(aRequest);
01071   ssl_thread_singleton->mPendingHTTPRequest = aRequest;
01072 }
01073 
01074 void nsSSLThread::cancelPendingHTTPRequest()
01075 {
01076   if (!ssl_thread_singleton)
01077     return;
01078 
01079   nsAutoLock threadLock(ssl_thread_singleton->mMutex);
01080 
01081   if (ssl_thread_singleton->mPendingHTTPRequest)
01082   {
01083     ssl_thread_singleton->mPendingHTTPRequest->Cancel(NS_ERROR_ABORT);
01084 
01085     NS_RELEASE(ssl_thread_singleton->mPendingHTTPRequest);
01086 
01087     ssl_thread_singleton->mPendingHTTPRequest = nsnull;
01088   }
01089 }
01090 
01091 nsSSLThread *nsSSLThread::ssl_thread_singleton = nsnull;