LCOV - code coverage report
Current view: directory - netwerk/base/src - nsServerSocket.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 167 133 79.6 %
Date: 2012-06-02 Functions: 33 32 97.0 %

       1                 : /* vim:set ts=2 sw=2 et cindent: */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is IBM Corporation.
      18                 :  * Portions created by IBM Corporation are Copyright (C) 2003
      19                 :  * IBM Corporation. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Darin Fisher <darin@meer.net>
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsIServiceManager.h"
      39                 : #include "nsSocketTransport2.h"
      40                 : #include "nsServerSocket.h"
      41                 : #include "nsProxyRelease.h"
      42                 : #include "nsAutoPtr.h"
      43                 : #include "nsNetError.h"
      44                 : #include "nsNetCID.h"
      45                 : #include "prnetdb.h"
      46                 : #include "prio.h"
      47                 : 
      48                 : using namespace mozilla;
      49                 : 
      50                 : static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
      51                 : 
      52                 : //-----------------------------------------------------------------------------
      53                 : 
      54                 : typedef void (nsServerSocket:: *nsServerSocketFunc)(void);
      55                 : 
      56                 : static nsresult
      57             828 : PostEvent(nsServerSocket *s, nsServerSocketFunc func)
      58                 : {
      59            1656 :   nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func);
      60             828 :   if (!ev)
      61               0 :     return NS_ERROR_OUT_OF_MEMORY;
      62                 : 
      63             828 :   if (!gSocketTransportService)
      64               0 :     return NS_ERROR_FAILURE;
      65                 : 
      66             828 :   return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
      67                 : }
      68                 : 
      69                 : //-----------------------------------------------------------------------------
      70                 : // nsServerSocket
      71                 : //-----------------------------------------------------------------------------
      72                 : 
      73             421 : nsServerSocket::nsServerSocket()
      74                 :   : mLock("nsServerSocket.mLock")
      75                 :   , mFD(nsnull)
      76             421 :   , mAttached(false)
      77                 : {
      78                 :   // we want to be able to access the STS directly, and it may not have been
      79                 :   // constructed yet.  the STS constructor sets gSocketTransportService.
      80             421 :   if (!gSocketTransportService)
      81                 :   {
      82                 :     // This call can fail if we're offline, for example.
      83                 :     nsCOMPtr<nsISocketTransportService> sts =
      84               0 :         do_GetService(kSocketTransportServiceCID);
      85                 :   }
      86                 :   // make sure the STS sticks around as long as we do
      87             421 :   NS_IF_ADDREF(gSocketTransportService);
      88             421 : }
      89                 : 
      90            1263 : nsServerSocket::~nsServerSocket()
      91                 : {
      92             421 :   Close(); // just in case :)
      93                 : 
      94                 :   // release our reference to the STS
      95             421 :   nsSocketTransportService *serv = gSocketTransportService;
      96             421 :   NS_IF_RELEASE(serv);
      97            1684 : }
      98                 : 
      99                 : void
     100             408 : nsServerSocket::OnMsgClose()
     101                 : {
     102             408 :   SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
     103                 : 
     104             408 :   if (NS_FAILED(mCondition))
     105               0 :     return;
     106                 : 
     107                 :   // tear down socket.  this signals the STS to detach our socket handler.
     108             408 :   mCondition = NS_BINDING_ABORTED;
     109                 : 
     110                 :   // if we are attached, then we'll close the socket in our OnSocketDetached.
     111                 :   // otherwise, call OnSocketDetached from here.
     112             408 :   if (!mAttached)
     113               0 :     OnSocketDetached(mFD);
     114                 : }
     115                 : 
     116                 : void
     117             420 : nsServerSocket::OnMsgAttach()
     118                 : {
     119             420 :   SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
     120                 : 
     121             420 :   if (NS_FAILED(mCondition))
     122               0 :     return;
     123                 : 
     124             420 :   mCondition = TryAttach();
     125                 :   
     126                 :   // if we hit an error while trying to attach then bail...
     127             420 :   if (NS_FAILED(mCondition))
     128                 :   {
     129               0 :     NS_ASSERTION(!mAttached, "should not be attached already");
     130               0 :     OnSocketDetached(mFD);
     131                 :   }
     132                 : }
     133                 : 
     134                 : nsresult
     135             420 : nsServerSocket::TryAttach()
     136                 : {
     137                 :   nsresult rv;
     138                 : 
     139             420 :   if (!gSocketTransportService)
     140               0 :     return NS_ERROR_FAILURE;
     141                 : 
     142                 :   //
     143                 :   // find out if it is going to be ok to attach another socket to the STS.
     144                 :   // if not then we have to wait for the STS to tell us that it is ok.
     145                 :   // the notification is asynchronous, which means that when we could be
     146                 :   // in a race to call AttachSocket once notified.  for this reason, when
     147                 :   // we get notified, we just re-enter this function.  as a result, we are
     148                 :   // sure to ask again before calling AttachSocket.  in this way we deal
     149                 :   // with the race condition.  though it isn't the most elegant solution,
     150                 :   // it is far simpler than trying to build a system that would guarantee
     151                 :   // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
     152                 :   // 194402 for more info.
     153                 :   //
     154             420 :   if (!gSocketTransportService->CanAttachSocket())
     155                 :   {
     156                 :     nsCOMPtr<nsIRunnable> event =
     157               0 :       NS_NewRunnableMethod(this, &nsServerSocket::OnMsgAttach);
     158               0 :     if (!event)
     159               0 :       return NS_ERROR_OUT_OF_MEMORY;
     160                 : 
     161               0 :     nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
     162               0 :     if (NS_FAILED(rv))
     163               0 :       return rv;
     164                 :   }
     165                 : 
     166                 :   //
     167                 :   // ok, we can now attach our socket to the STS for polling
     168                 :   //
     169             420 :   rv = gSocketTransportService->AttachSocket(mFD, this);
     170             420 :   if (NS_FAILED(rv))
     171               0 :     return rv;
     172                 : 
     173             420 :   mAttached = true;
     174                 : 
     175                 :   //
     176                 :   // now, configure our poll flags for listening...
     177                 :   //
     178             420 :   mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
     179             420 :   return NS_OK;
     180                 : }
     181                 : 
     182                 : //-----------------------------------------------------------------------------
     183                 : // nsServerSocket::nsASocketHandler
     184                 : //-----------------------------------------------------------------------------
     185                 : 
     186                 : void
     187            2870 : nsServerSocket::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
     188                 : {
     189            2870 :   NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
     190            2870 :   NS_ASSERTION(mFD == fd, "wrong file descriptor");
     191            2870 :   NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
     192                 : 
     193            2870 :   if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
     194                 :   {
     195               0 :     NS_WARNING("error polling on listening socket");
     196               0 :     mCondition = NS_ERROR_UNEXPECTED;
     197               0 :     return;
     198                 :   }
     199                 : 
     200                 :   PRFileDesc *clientFD;
     201                 :   PRNetAddr clientAddr;
     202                 : 
     203            2870 :   clientFD = PR_Accept(mFD, &clientAddr, PR_INTERVAL_NO_WAIT);
     204            2870 :   if (!clientFD)
     205                 :   {
     206               0 :     NS_WARNING("PR_Accept failed");
     207               0 :     mCondition = NS_ERROR_UNEXPECTED;
     208                 :   }
     209                 :   else
     210                 :   {
     211            5740 :     nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
     212            2870 :     if (!trans)
     213               0 :       mCondition = NS_ERROR_OUT_OF_MEMORY;
     214                 :     else
     215                 :     {
     216            2870 :       nsresult rv = trans->InitWithConnectedSocket(clientFD, &clientAddr);
     217            2870 :       if (NS_FAILED(rv))
     218               0 :         mCondition = rv;
     219                 :       else
     220            2870 :         mListener->OnSocketAccepted(this, trans);
     221                 :     }
     222                 :   }
     223                 : }
     224                 : 
     225                 : void
     226             420 : nsServerSocket::OnSocketDetached(PRFileDesc *fd)
     227                 : {
     228                 :   // force a failure condition if none set; maybe the STS is shutting down :-/
     229             420 :   if (NS_SUCCEEDED(mCondition))
     230              12 :     mCondition = NS_ERROR_ABORT;
     231                 : 
     232             420 :   if (mFD)
     233                 :   {
     234             420 :     NS_ASSERTION(mFD == fd, "wrong file descriptor");
     235             420 :     PR_Close(mFD);
     236             420 :     mFD = nsnull;
     237                 :   }
     238                 : 
     239             420 :   if (mListener)
     240                 :   {
     241             420 :     mListener->OnStopListening(this, mCondition);
     242                 : 
     243                 :     // need to atomically clear mListener.  see our Close() method.
     244             420 :     nsIServerSocketListener *listener = nsnull;
     245                 :     {
     246             840 :       MutexAutoLock lock(mLock);
     247             420 :       mListener.swap(listener);
     248                 :     }
     249                 :     // XXX we need to proxy the release to the listener's target thread to work
     250                 :     // around bug 337492.
     251             420 :     if (listener)
     252             420 :       NS_ProxyRelease(mListenerTarget, listener);
     253                 :   }
     254             420 : }
     255                 : 
     256                 : 
     257                 : //-----------------------------------------------------------------------------
     258                 : // nsServerSocket::nsISupports
     259                 : //-----------------------------------------------------------------------------
     260                 : 
     261           51085 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsServerSocket, nsIServerSocket)
     262                 : 
     263                 : 
     264                 : //-----------------------------------------------------------------------------
     265                 : // nsServerSocket::nsIServerSocket
     266                 : //-----------------------------------------------------------------------------
     267                 : 
     268                 : NS_IMETHODIMP
     269             421 : nsServerSocket::Init(PRInt32 aPort, bool aLoopbackOnly, PRInt32 aBackLog)
     270                 : {
     271                 :   PRNetAddrValue val;
     272                 :   PRNetAddr addr;
     273                 : 
     274             421 :   if (aPort < 0)
     275               2 :     aPort = 0;
     276             421 :   if (aLoopbackOnly)
     277             421 :     val = PR_IpAddrLoopback;
     278                 :   else
     279               0 :     val = PR_IpAddrAny;
     280             421 :   PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
     281                 : 
     282             421 :   return InitWithAddress(&addr, aBackLog);
     283                 : }
     284                 : 
     285                 : NS_IMETHODIMP
     286             421 : nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, PRInt32 aBackLog)
     287                 : {
     288             421 :   NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
     289                 : 
     290                 :   //
     291                 :   // configure listening socket...
     292                 :   //
     293                 : 
     294             421 :   mFD = PR_OpenTCPSocket(aAddr->raw.family);
     295             421 :   if (!mFD)
     296                 :   {
     297               0 :     NS_WARNING("unable to create server socket");
     298               0 :     return NS_ERROR_FAILURE;
     299                 :   }
     300                 : 
     301                 :   PRSocketOptionData opt;
     302                 : 
     303             421 :   opt.option = PR_SockOpt_Reuseaddr;
     304             421 :   opt.value.reuse_addr = true;
     305             421 :   PR_SetSocketOption(mFD, &opt);
     306                 : 
     307             421 :   opt.option = PR_SockOpt_Nonblocking;
     308             421 :   opt.value.non_blocking = true;
     309             421 :   PR_SetSocketOption(mFD, &opt);
     310                 : 
     311             421 :   if (PR_Bind(mFD, aAddr) != PR_SUCCESS)
     312                 :   {
     313               1 :     NS_WARNING("failed to bind socket");
     314               1 :     goto fail;
     315                 :   }
     316                 : 
     317             420 :   if (aBackLog < 0)
     318               2 :     aBackLog = 5; // seems like a reasonable default
     319                 : 
     320             420 :   if (PR_Listen(mFD, aBackLog) != PR_SUCCESS)
     321                 :   {
     322               0 :     NS_WARNING("cannot listen on socket");
     323               0 :     goto fail;
     324                 :   }
     325                 : 
     326                 :   // get the resulting socket address, which may be different than what
     327                 :   // we passed to bind.
     328             420 :   if (PR_GetSockName(mFD, &mAddr) != PR_SUCCESS)
     329                 :   {
     330               0 :     NS_WARNING("cannot get socket name");
     331               0 :     goto fail;
     332                 :   }
     333                 : 
     334                 :   // wait until AsyncListen is called before polling the socket for
     335                 :   // client connections.
     336             420 :   return NS_OK;
     337                 : 
     338                 : fail:
     339               1 :   Close();
     340               1 :   return NS_ERROR_FAILURE;
     341                 : }
     342                 : 
     343                 : NS_IMETHODIMP
     344             830 : nsServerSocket::Close()
     345                 : {
     346                 :   {
     347            1660 :     MutexAutoLock lock(mLock);
     348                 :     // we want to proxy the close operation to the socket thread if a listener
     349                 :     // has been set.  otherwise, we should just close the socket here...
     350             830 :     if (!mListener)
     351                 :     {
     352             422 :       if (mFD)
     353                 :       {
     354               1 :         PR_Close(mFD);
     355               1 :         mFD = nsnull;
     356                 :       }
     357             422 :       return NS_OK;
     358                 :     }
     359                 :   }
     360             408 :   return PostEvent(this, &nsServerSocket::OnMsgClose);
     361                 : }
     362                 : 
     363                 : namespace {
     364                 : 
     365                 : class ServerSocketListenerProxy : public nsIServerSocketListener
     366             420 : {
     367                 : public:
     368             420 :   ServerSocketListenerProxy(nsIServerSocketListener* aListener)
     369                 :     : mListener(aListener)
     370             420 :     , mTargetThread(do_GetCurrentThread())
     371             420 :   { }
     372                 : 
     373                 :   NS_DECL_ISUPPORTS
     374                 :   NS_DECL_NSISERVERSOCKETLISTENER
     375                 : 
     376                 :   class OnSocketAcceptedRunnable : public nsRunnable
     377           11480 :   {
     378                 :   public:
     379            2870 :     OnSocketAcceptedRunnable(nsIServerSocketListener* aListener,
     380                 :                              nsIServerSocket* aServ,
     381                 :                              nsISocketTransport* aTransport)
     382                 :       : mListener(aListener)
     383                 :       , mServ(aServ)
     384            2870 :       , mTransport(aTransport)
     385            2870 :     { }
     386                 :     
     387                 :     NS_DECL_NSIRUNNABLE
     388                 : 
     389                 :   private:
     390                 :     nsCOMPtr<nsIServerSocketListener> mListener;
     391                 :     nsCOMPtr<nsIServerSocket> mServ;
     392                 :     nsCOMPtr<nsISocketTransport> mTransport;
     393                 :   };
     394                 : 
     395                 :   class OnStopListeningRunnable : public nsRunnable
     396            1680 :   {
     397                 :   public:
     398             420 :     OnStopListeningRunnable(nsIServerSocketListener* aListener,
     399                 :                             nsIServerSocket* aServ,
     400                 :                             nsresult aStatus)
     401                 :       : mListener(aListener)
     402                 :       , mServ(aServ)
     403             420 :       , mStatus(aStatus)
     404             420 :     { }
     405                 : 
     406                 :     NS_DECL_NSIRUNNABLE
     407                 : 
     408                 :   private:
     409                 :     nsCOMPtr<nsIServerSocketListener> mListener;
     410                 :     nsCOMPtr<nsIServerSocket> mServ;
     411                 :     nsresult mStatus;
     412                 :   };
     413                 : 
     414                 : private:
     415                 :   nsCOMPtr<nsIServerSocketListener> mListener;
     416                 :   nsCOMPtr<nsIEventTarget> mTargetThread;
     417                 : };
     418                 : 
     419            2520 : NS_IMPL_THREADSAFE_ISUPPORTS1(ServerSocketListenerProxy,
     420                 :                               nsIServerSocketListener)
     421                 : 
     422                 : NS_IMETHODIMP
     423            2870 : ServerSocketListenerProxy::OnSocketAccepted(nsIServerSocket* aServ,
     424                 :                                             nsISocketTransport* aTransport)
     425                 : {
     426                 :   nsRefPtr<OnSocketAcceptedRunnable> r =
     427            8610 :     new OnSocketAcceptedRunnable(mListener, aServ, aTransport);
     428            2870 :   return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
     429                 : }
     430                 : 
     431                 : NS_IMETHODIMP
     432             420 : ServerSocketListenerProxy::OnStopListening(nsIServerSocket* aServ,
     433                 :                                            nsresult aStatus)
     434                 : {
     435                 :   nsRefPtr<OnStopListeningRunnable> r =
     436            1260 :     new OnStopListeningRunnable(mListener, aServ, aStatus);
     437             420 :   return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
     438                 : }
     439                 : 
     440                 : NS_IMETHODIMP
     441            2870 : ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run()
     442                 : {
     443            2870 :   mListener->OnSocketAccepted(mServ, mTransport);
     444            2870 :   return NS_OK;
     445                 : }
     446                 : 
     447                 : NS_IMETHODIMP
     448             420 : ServerSocketListenerProxy::OnStopListeningRunnable::Run()
     449                 : {
     450             420 :   mListener->OnStopListening(mServ, mStatus);
     451             420 :   return NS_OK;
     452                 : }
     453                 : 
     454                 : } // anonymous namespace
     455                 : 
     456                 : NS_IMETHODIMP
     457             420 : nsServerSocket::AsyncListen(nsIServerSocketListener *aListener)
     458                 : {
     459                 :   // ensuring mFD implies ensuring mLock
     460             420 :   NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
     461             420 :   NS_ENSURE_TRUE(mListener == nsnull, NS_ERROR_IN_PROGRESS);
     462                 :   {
     463             840 :     MutexAutoLock lock(mLock);
     464             420 :     mListener = new ServerSocketListenerProxy(aListener);
     465             420 :     mListenerTarget = NS_GetCurrentThread();
     466                 :   }
     467             420 :   return PostEvent(this, &nsServerSocket::OnMsgAttach);
     468                 : }
     469                 : 
     470                 : NS_IMETHODIMP
     471            4098 : nsServerSocket::GetPort(PRInt32 *aResult)
     472                 : {
     473                 :   // no need to enter the lock here
     474                 :   PRUint16 port;
     475            4098 :   if (mAddr.raw.family == PR_AF_INET)
     476            4098 :     port = mAddr.inet.port;
     477                 :   else
     478               0 :     port = mAddr.ipv6.port;
     479            4098 :   *aResult = (PRInt32) PR_ntohs(port);
     480            4098 :   return NS_OK;
     481                 : }
     482                 : 
     483                 : NS_IMETHODIMP
     484               0 : nsServerSocket::GetAddress(PRNetAddr *aResult)
     485                 : {
     486                 :   // no need to enter the lock here
     487               0 :   memcpy(aResult, &mAddr, sizeof(mAddr));
     488               0 :   return NS_OK;
     489                 : }

Generated by: LCOV version 1.7