1 /* vim:set ts=2 sw=2 et cindent: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
15 * The Original Code is Mozilla.
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.
22 * Darin Fisher <darin@meer.net>
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.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsIProxyObjectManager.h"
39 #include "nsIServiceManager.h"
40 #include "nsSocketTransport2.h"
41 #include "nsServerSocket.h"
42 #include "nsProxyRelease.h"
43 #include "nsAutoLock.h"
44 #include "nsAutoPtr.h"
45 #include "nsNetError.h"
50 static NS_DEFINE_CID(kSocketTransportServiceCID
, NS_SOCKETTRANSPORTSERVICE_CID
);
52 //-----------------------------------------------------------------------------
54 typedef void (nsServerSocket:: *nsServerSocketFunc
)(void);
57 PostEvent(nsServerSocket
*s
, nsServerSocketFunc func
)
59 nsCOMPtr
<nsIRunnable
> ev
= new nsRunnableMethod
<nsServerSocket
>(s
, func
);
61 return NS_ERROR_OUT_OF_MEMORY
;
63 return gSocketTransportService
->Dispatch(ev
, NS_DISPATCH_NORMAL
);
66 //-----------------------------------------------------------------------------
68 //-----------------------------------------------------------------------------
70 nsServerSocket::nsServerSocket()
75 // we want to be able to access the STS directly, and it may not have been
76 // constructed yet. the STS constructor sets gSocketTransportService.
77 if (!gSocketTransportService
)
79 nsCOMPtr
<nsISocketTransportService
> sts
=
80 do_GetService(kSocketTransportServiceCID
);
81 NS_ASSERTION(sts
, "no socket transport service");
83 // make sure the STS sticks around as long as we do
84 NS_ADDREF(gSocketTransportService
);
87 nsServerSocket::~nsServerSocket()
89 Close(); // just in case :)
92 PR_DestroyLock(mLock
);
94 // release our reference to the STS
95 nsSocketTransportService
*serv
= gSocketTransportService
;
100 nsServerSocket::OnMsgClose()
102 LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
104 if (NS_FAILED(mCondition
))
107 // tear down socket. this signals the STS to detach our socket handler.
108 mCondition
= NS_BINDING_ABORTED
;
110 // if we are attached, then we'll close the socket in our OnSocketDetached.
111 // otherwise, call OnSocketDetached from here.
113 OnSocketDetached(mFD
);
117 nsServerSocket::OnMsgAttach()
119 LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
121 if (NS_FAILED(mCondition
))
124 mCondition
= TryAttach();
126 // if we hit an error while trying to attach then bail...
127 if (NS_FAILED(mCondition
))
129 NS_ASSERTION(!mAttached
, "should not be attached already");
130 OnSocketDetached(mFD
);
135 nsServerSocket::TryAttach()
140 // find out if it is going to be ok to attach another socket to the STS.
141 // if not then we have to wait for the STS to tell us that it is ok.
142 // the notification is asynchronous, which means that when we could be
143 // in a race to call AttachSocket once notified. for this reason, when
144 // we get notified, we just re-enter this function. as a result, we are
145 // sure to ask again before calling AttachSocket. in this way we deal
146 // with the race condition. though it isn't the most elegant solution,
147 // it is far simpler than trying to build a system that would guarantee
148 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
149 // 194402 for more info.
151 if (!gSocketTransportService
->CanAttachSocket())
153 nsCOMPtr
<nsIRunnable
> event
=
154 NS_NEW_RUNNABLE_METHOD(nsServerSocket
, this, OnMsgAttach
);
156 return NS_ERROR_OUT_OF_MEMORY
;
158 nsresult rv
= gSocketTransportService
->NotifyWhenCanAttachSocket(event
);
164 // ok, we can now attach our socket to the STS for polling
166 rv
= gSocketTransportService
->AttachSocket(mFD
, this);
173 // now, configure our poll flags for listening...
175 mPollFlags
= (PR_POLL_READ
| PR_POLL_EXCEPT
);
179 //-----------------------------------------------------------------------------
180 // nsServerSocket::nsASocketHandler
181 //-----------------------------------------------------------------------------
184 nsServerSocket::OnSocketReady(PRFileDesc
*fd
, PRInt16 outFlags
)
186 NS_ASSERTION(NS_SUCCEEDED(mCondition
), "oops");
187 NS_ASSERTION(mFD
== fd
, "wrong file descriptor");
188 NS_ASSERTION(outFlags
!= -1, "unexpected timeout condition reached");
190 if (outFlags
& (PR_POLL_ERR
| PR_POLL_HUP
| PR_POLL_NVAL
))
192 NS_WARNING("error polling on listening socket");
193 mCondition
= NS_ERROR_UNEXPECTED
;
197 PRFileDesc
*clientFD
;
198 PRNetAddr clientAddr
;
200 clientFD
= PR_Accept(mFD
, &clientAddr
, PR_INTERVAL_NO_WAIT
);
203 NS_WARNING("PR_Accept failed");
204 mCondition
= NS_ERROR_UNEXPECTED
;
208 nsRefPtr
<nsSocketTransport
> trans
= new nsSocketTransport
;
210 mCondition
= NS_ERROR_OUT_OF_MEMORY
;
213 nsresult rv
= trans
->InitWithConnectedSocket(clientFD
, &clientAddr
);
217 mListener
->OnSocketAccepted(this, trans
);
223 nsServerSocket::OnSocketDetached(PRFileDesc
*fd
)
225 // force a failure condition if none set; maybe the STS is shutting down :-/
226 if (NS_SUCCEEDED(mCondition
))
227 mCondition
= NS_ERROR_ABORT
;
231 NS_ASSERTION(mFD
== fd
, "wrong file descriptor");
238 mListener
->OnStopListening(this, mCondition
);
240 // need to atomically clear mListener. see our Close() method.
241 nsIServerSocketListener
*listener
= nsnull
;
243 nsAutoLock
lock(mLock
);
244 mListener
.swap(listener
);
246 // XXX we need to proxy the release to the listener's target thread to work
247 // around bug 337492.
249 NS_ProxyRelease(mListenerTarget
, listener
);
254 //-----------------------------------------------------------------------------
255 // nsServerSocket::nsISupports
256 //-----------------------------------------------------------------------------
258 NS_IMPL_THREADSAFE_ISUPPORTS1(nsServerSocket
, nsIServerSocket
)
261 //-----------------------------------------------------------------------------
262 // nsServerSocket::nsIServerSocket
263 //-----------------------------------------------------------------------------
266 nsServerSocket::Init(PRInt32 aPort
, PRBool aLoopbackOnly
, PRInt32 aBackLog
)
274 val
= PR_IpAddrLoopback
;
277 PR_SetNetAddr(val
, PR_AF_INET
, aPort
, &addr
);
279 return InitWithAddress(&addr
, aBackLog
);
283 nsServerSocket::InitWithAddress(const PRNetAddr
*aAddr
, PRInt32 aBackLog
)
285 NS_ENSURE_TRUE(mFD
== nsnull
, NS_ERROR_ALREADY_INITIALIZED
);
289 mLock
= PR_NewLock();
291 return NS_ERROR_OUT_OF_MEMORY
;
295 // configure listening socket...
298 mFD
= PR_OpenTCPSocket(aAddr
->raw
.family
);
301 NS_WARNING("unable to create server socket");
302 return NS_ERROR_FAILURE
;
305 PRSocketOptionData opt
;
307 opt
.option
= PR_SockOpt_Reuseaddr
;
308 opt
.value
.reuse_addr
= PR_TRUE
;
309 PR_SetSocketOption(mFD
, &opt
);
311 opt
.option
= PR_SockOpt_Nonblocking
;
312 opt
.value
.non_blocking
= PR_TRUE
;
313 PR_SetSocketOption(mFD
, &opt
);
315 if (PR_Bind(mFD
, aAddr
) != PR_SUCCESS
)
317 NS_WARNING("failed to bind socket");
322 aBackLog
= 5; // seems like a reasonable default
324 if (PR_Listen(mFD
, aBackLog
) != PR_SUCCESS
)
326 NS_WARNING("cannot listen on socket");
330 // get the resulting socket address, which may be different than what
331 // we passed to bind.
332 if (PR_GetSockName(mFD
, &mAddr
) != PR_SUCCESS
)
334 NS_WARNING("cannot get socket name");
338 // wait until AsyncListen is called before polling the socket for
339 // client connections.
344 return NS_ERROR_FAILURE
;
348 nsServerSocket::Close()
350 NS_ENSURE_TRUE(mLock
, NS_ERROR_NOT_INITIALIZED
);
352 nsAutoLock
lock(mLock
);
353 // we want to proxy the close operation to the socket thread if a listener
354 // has been set. otherwise, we should just close the socket here...
365 return PostEvent(this, &nsServerSocket::OnMsgClose
);
369 nsServerSocket::AsyncListen(nsIServerSocketListener
*aListener
)
371 // ensuring mFD implies ensuring mLock
372 NS_ENSURE_TRUE(mFD
, NS_ERROR_NOT_INITIALIZED
);
373 NS_ENSURE_TRUE(mListener
== nsnull
, NS_ERROR_IN_PROGRESS
);
375 nsAutoLock
lock(mLock
);
376 nsresult rv
= NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD
,
377 NS_GET_IID(nsIServerSocketListener
),
379 NS_PROXY_ASYNC
| NS_PROXY_ALWAYS
,
380 getter_AddRefs(mListener
));
383 mListenerTarget
= NS_GetCurrentThread();
385 return PostEvent(this, &nsServerSocket::OnMsgAttach
);
389 nsServerSocket::GetPort(PRInt32
*aResult
)
391 // no need to enter the lock here
393 if (mAddr
.raw
.family
== PR_AF_INET
)
394 port
= mAddr
.inet
.port
;
396 port
= mAddr
.ipv6
.port
;
397 *aResult
= (PRInt32
) PR_ntohs(port
);
402 nsServerSocket::GetAddress(PRNetAddr
*aResult
)
404 // no need to enter the lock here
405 memcpy(aResult
, &mAddr
, sizeof(mAddr
));