Back to index

lightning-sunbird  0.9+nobinonly
nsServerSocket.cpp
Go to the documentation of this file.
00001 /* vim:set ts=2 sw=2 et cindent: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by IBM Corporation are Copyright (C) 2003
00019  * IBM Corporation. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@meer.net>
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 "nsIProxyObjectManager.h"
00039 #include "nsIServiceManager.h"
00040 #include "nsSocketTransport2.h"
00041 #include "nsServerSocket.h"
00042 #include "nsAutoLock.h"
00043 #include "nsAutoPtr.h"
00044 #include "nsNetError.h"
00045 #include "nsNetCID.h"
00046 #include "prnetdb.h"
00047 #include "prio.h"
00048 
00049 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00050 
00051 //-----------------------------------------------------------------------------
00052 
00053 typedef void (nsServerSocket:: *nsServerSocketFunc)(void);
00054 
00055 struct nsServerSocketEvent : PLEvent
00056 {
00057   nsServerSocketEvent(nsServerSocket *s, nsServerSocketFunc f)
00058     : func(f)
00059   {
00060     NS_ADDREF(s);
00061     PL_InitEvent(this, s, EventHandler, EventCleanup);
00062   }
00063 
00064   PR_STATIC_CALLBACK(void *)
00065   EventHandler(PLEvent *ev)
00066   {
00067     nsServerSocket *s = (nsServerSocket *) ev->owner;
00068     nsServerSocketEvent *event = (nsServerSocketEvent *) ev;
00069     nsServerSocketFunc func = event->func;
00070     (s->*func)();
00071     return nsnull;
00072   }
00073 
00074   PR_STATIC_CALLBACK(void)
00075   EventCleanup(PLEvent *ev)
00076   {
00077     nsServerSocket *s = (nsServerSocket *) ev->owner;
00078     NS_RELEASE(s);
00079     delete (nsServerSocketEvent *) ev;
00080   }
00081 
00082   nsServerSocketFunc func;
00083 };
00084 
00085 static nsresult
00086 PostEvent(nsServerSocket *s, nsServerSocketFunc func)
00087 {
00088   nsServerSocketEvent *ev = new nsServerSocketEvent(s, func);
00089   if (!ev)
00090     return NS_ERROR_OUT_OF_MEMORY;
00091 
00092   nsresult rv = gSocketTransportService->PostEvent(ev);
00093   if (NS_FAILED(rv))
00094     PL_DestroyEvent(ev);
00095   return rv;
00096 }
00097 
00098 //-----------------------------------------------------------------------------
00099 // nsServerSocket
00100 //-----------------------------------------------------------------------------
00101 
00102 nsServerSocket::nsServerSocket()
00103   : mLock(nsnull)
00104   , mFD(nsnull)
00105   , mAttached(PR_FALSE)
00106 {
00107   // we want to be able to access the STS directly, and it may not have been
00108   // constructed yet.  the STS constructor sets gSocketTransportService.
00109   if (!gSocketTransportService)
00110   {
00111     nsCOMPtr<nsISocketTransportService> sts =
00112         do_GetService(kSocketTransportServiceCID);
00113     NS_ASSERTION(sts, "no socket transport service");
00114   }
00115   // make sure the STS sticks around as long as we do
00116   NS_ADDREF(gSocketTransportService);
00117 }
00118 
00119 nsServerSocket::~nsServerSocket()
00120 {
00121   Close(); // just in case :)
00122 
00123   if (mLock)
00124     PR_DestroyLock(mLock);
00125 
00126   // release our reference to the STS
00127   nsSocketTransportService *serv = gSocketTransportService;
00128   NS_RELEASE(serv);
00129 }
00130 
00131 void
00132 nsServerSocket::OnMsgClose()
00133 {
00134   LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
00135 
00136   if (NS_FAILED(mCondition))
00137     return;
00138 
00139   // tear down socket.  this signals the STS to detach our socket handler.
00140   mCondition = NS_BINDING_ABORTED;
00141 
00142   // if we are attached, then we'll close the socket in our OnSocketDetached.
00143   // otherwise, call OnSocketDetached from here.
00144   if (!mAttached)
00145     OnSocketDetached(mFD);
00146 }
00147 
00148 void
00149 nsServerSocket::OnMsgAttach()
00150 {
00151   LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
00152 
00153   if (NS_FAILED(mCondition))
00154     return;
00155 
00156   mCondition = TryAttach();
00157   
00158   // if we hit an error while trying to attach then bail...
00159   if (NS_FAILED(mCondition))
00160   {
00161     NS_ASSERTION(!mAttached, "should not be attached already");
00162     OnSocketDetached(mFD);
00163   }
00164 }
00165 
00166 nsresult
00167 nsServerSocket::TryAttach()
00168 {
00169   nsresult rv;
00170 
00171   //
00172   // find out if it is going to be ok to attach another socket to the STS.
00173   // if not then we have to wait for the STS to tell us that it is ok.
00174   // the notification is asynchronous, which means that when we could be
00175   // in a race to call AttachSocket once notified.  for this reason, when
00176   // we get notified, we just re-enter this function.  as a result, we are
00177   // sure to ask again before calling AttachSocket.  in this way we deal
00178   // with the race condition.  though it isn't the most elegant solution,
00179   // it is far simpler than trying to build a system that would guarantee
00180   // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
00181   // 194402 for more info.
00182   //
00183   if (!gSocketTransportService->CanAttachSocket())
00184   {
00185     PLEvent *event = new nsServerSocketEvent(this, &nsServerSocket::OnMsgAttach);
00186     if (!event)
00187       return NS_ERROR_OUT_OF_MEMORY;
00188 
00189     nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
00190     if (NS_FAILED(rv))
00191     {
00192       PL_DestroyEvent(event);
00193       return rv;
00194     }
00195   }
00196 
00197   //
00198   // ok, we can now attach our socket to the STS for polling
00199   //
00200   rv = gSocketTransportService->AttachSocket(mFD, this);
00201   if (NS_FAILED(rv))
00202     return rv;
00203 
00204   mAttached = PR_TRUE;
00205 
00206   //
00207   // now, configure our poll flags for listening...
00208   //
00209   mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
00210   return NS_OK;
00211 }
00212 
00213 //-----------------------------------------------------------------------------
00214 // nsServerSocket::nsASocketHandler
00215 //-----------------------------------------------------------------------------
00216 
00217 void
00218 nsServerSocket::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
00219 {
00220   NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
00221   NS_ASSERTION(mFD == fd, "wrong file descriptor");
00222   NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
00223 
00224   if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
00225   {
00226     NS_WARNING("error polling on listening socket");
00227     mCondition = NS_ERROR_UNEXPECTED;
00228     return;
00229   }
00230 
00231   PRFileDesc *clientFD;
00232   PRNetAddr clientAddr;
00233 
00234   clientFD = PR_Accept(mFD, &clientAddr, PR_INTERVAL_NO_WAIT);
00235   if (!clientFD)
00236   {
00237     NS_WARNING("PR_Accept failed");
00238     mCondition = NS_ERROR_UNEXPECTED;
00239   }
00240   else
00241   {
00242     nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
00243     if (!trans)
00244       mCondition = NS_ERROR_OUT_OF_MEMORY;
00245     else
00246     {
00247       nsresult rv = trans->InitWithConnectedSocket(clientFD, &clientAddr);
00248       if (NS_FAILED(rv))
00249         mCondition = rv;
00250       else
00251         mListener->OnSocketAccepted(this, trans);
00252     }
00253   }
00254 }
00255 
00256 void
00257 nsServerSocket::OnSocketDetached(PRFileDesc *fd)
00258 {
00259   // force a failure condition if none set; maybe the STS is shutting down :-/
00260   if (NS_SUCCEEDED(mCondition))
00261     mCondition = NS_ERROR_ABORT;
00262 
00263   if (mFD)
00264   {
00265     NS_ASSERTION(mFD == fd, "wrong file descriptor");
00266     PR_Close(mFD);
00267     mFD = nsnull;
00268   }
00269 
00270   if (mListener)
00271   {
00272     mListener->OnStopListening(this, mCondition);
00273 
00274     // need to atomically clear mListener.  see our Close() method.
00275     nsAutoLock lock(mLock);
00276     mListener = nsnull;
00277   }
00278 }
00279 
00280 
00281 //-----------------------------------------------------------------------------
00282 // nsServerSocket::nsISupports
00283 //-----------------------------------------------------------------------------
00284 
00285 NS_IMPL_THREADSAFE_ISUPPORTS1(nsServerSocket, nsIServerSocket)
00286 
00287 
00288 //-----------------------------------------------------------------------------
00289 // nsServerSocket::nsIServerSocket
00290 //-----------------------------------------------------------------------------
00291 
00292 NS_IMETHODIMP
00293 nsServerSocket::Init(PRInt32 aPort, PRBool aLoopbackOnly, PRInt32 aBackLog)
00294 {
00295   PRNetAddrValue val;
00296   PRNetAddr addr;
00297 
00298   if (aPort < 0)
00299     aPort = 0;
00300   if (aLoopbackOnly)
00301     val = PR_IpAddrLoopback;
00302   else
00303     val = PR_IpAddrAny;
00304   PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
00305 
00306   return InitWithAddress(&addr, aBackLog);
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, PRInt32 aBackLog)
00311 {
00312   NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
00313 
00314   if (!mLock)
00315   {
00316     mLock = PR_NewLock();
00317     if (!mLock)
00318       return NS_ERROR_OUT_OF_MEMORY;
00319   }
00320 
00321   //
00322   // configure listening socket...
00323   //
00324 
00325   mFD = PR_OpenTCPSocket(aAddr->raw.family);
00326   if (!mFD)
00327   {
00328     NS_WARNING("unable to create server socket");
00329     return NS_ERROR_FAILURE;
00330   }
00331 
00332   PRSocketOptionData opt;
00333 
00334   opt.option = PR_SockOpt_Reuseaddr;
00335   opt.value.reuse_addr = PR_TRUE;
00336   PR_SetSocketOption(mFD, &opt);
00337 
00338   opt.option = PR_SockOpt_Nonblocking;
00339   opt.value.non_blocking = PR_TRUE;
00340   PR_SetSocketOption(mFD, &opt);
00341 
00342   if (PR_Bind(mFD, aAddr) != PR_SUCCESS)
00343   {
00344     NS_WARNING("failed to bind socket");
00345     goto fail;
00346   }
00347 
00348   if (aBackLog < 0)
00349     aBackLog = 5; // seems like a reasonable default
00350 
00351   if (PR_Listen(mFD, aBackLog) != PR_SUCCESS)
00352   {
00353     NS_WARNING("cannot listen on socket");
00354     goto fail;
00355   }
00356 
00357   // get the resulting socket address, which may be different than what
00358   // we passed to bind.
00359   if (PR_GetSockName(mFD, &mAddr) != PR_SUCCESS)
00360   {
00361     NS_WARNING("cannot get socket name");
00362     goto fail;
00363   }
00364 
00365   // wait until AsyncListen is called before polling the socket for
00366   // client connections.
00367   return NS_OK;
00368 
00369 fail:
00370   Close();
00371   return NS_ERROR_FAILURE;
00372 }
00373 
00374 NS_IMETHODIMP
00375 nsServerSocket::Close()
00376 {
00377   NS_ENSURE_TRUE(mLock, NS_ERROR_NOT_INITIALIZED);
00378   {
00379     nsAutoLock lock(mLock);
00380     // we want to proxy the close operation to the socket thread if a listener
00381     // has been set.  otherwise, we should just close the socket here...
00382     if (!mListener)
00383     {
00384       if (mFD)
00385       {
00386         PR_Close(mFD);
00387         mFD = nsnull;
00388       }
00389       return NS_OK;
00390     }
00391   }
00392   return PostEvent(this, &nsServerSocket::OnMsgClose);
00393 }
00394 
00395 NS_IMETHODIMP
00396 nsServerSocket::AsyncListen(nsIServerSocketListener *aListener)
00397 {
00398   // ensuring mFD implies ensuring mLock
00399   NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
00400   NS_ENSURE_TRUE(mListener == nsnull, NS_ERROR_IN_PROGRESS);
00401   {
00402     nsAutoLock lock(mLock);
00403     nsresult rv = NS_GetProxyForObject(NS_CURRENT_EVENTQ,
00404                                        NS_GET_IID(nsIServerSocketListener),
00405                                        aListener,
00406                                        PROXY_ASYNC | PROXY_ALWAYS,
00407                                        getter_AddRefs(mListener));
00408     if (NS_FAILED(rv))
00409       return rv;
00410   }
00411   return PostEvent(this, &nsServerSocket::OnMsgAttach);
00412 }
00413 
00414 NS_IMETHODIMP
00415 nsServerSocket::GetPort(PRInt32 *aResult)
00416 {
00417   // no need to enter the lock here
00418   PRUint16 port;
00419   if (mAddr.raw.family == PR_AF_INET)
00420     port = mAddr.inet.port;
00421   else
00422     port = mAddr.ipv6.port;
00423   *aResult = (PRInt32) PR_ntohs(port);
00424   return NS_OK;
00425 }
00426 
00427 NS_IMETHODIMP
00428 nsServerSocket::GetAddress(PRNetAddr *aResult)
00429 {
00430   // no need to enter the lock here
00431   memcpy(aResult, &mAddr, sizeof(mAddr));
00432   return NS_OK;
00433 }