Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / netwerk / base / src / nsServerSocket.cpp
blob82f3e909036975231fdbc505828b7c5ccdfcc1be
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
13 * License.
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.
21 * Contributor(s):
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"
46 #include "nsNetCID.h"
47 #include "prnetdb.h"
48 #include "prio.h"
50 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
52 //-----------------------------------------------------------------------------
54 typedef void (nsServerSocket:: *nsServerSocketFunc)(void);
56 static nsresult
57 PostEvent(nsServerSocket *s, nsServerSocketFunc func)
59 nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func);
60 if (!ev)
61 return NS_ERROR_OUT_OF_MEMORY;
63 return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
66 //-----------------------------------------------------------------------------
67 // nsServerSocket
68 //-----------------------------------------------------------------------------
70 nsServerSocket::nsServerSocket()
71 : mLock(nsnull)
72 , mFD(nsnull)
73 , mAttached(PR_FALSE)
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 :)
91 if (mLock)
92 PR_DestroyLock(mLock);
94 // release our reference to the STS
95 nsSocketTransportService *serv = gSocketTransportService;
96 NS_RELEASE(serv);
99 void
100 nsServerSocket::OnMsgClose()
102 SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
104 if (NS_FAILED(mCondition))
105 return;
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.
112 if (!mAttached)
113 OnSocketDetached(mFD);
116 void
117 nsServerSocket::OnMsgAttach()
119 SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
121 if (NS_FAILED(mCondition))
122 return;
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);
134 nsresult
135 nsServerSocket::TryAttach()
137 nsresult rv;
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_NewRunnableMethod(this, &nsServerSocket::OnMsgAttach);
155 if (!event)
156 return NS_ERROR_OUT_OF_MEMORY;
158 nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
159 if (NS_FAILED(rv))
160 return rv;
164 // ok, we can now attach our socket to the STS for polling
166 rv = gSocketTransportService->AttachSocket(mFD, this);
167 if (NS_FAILED(rv))
168 return rv;
170 mAttached = PR_TRUE;
173 // now, configure our poll flags for listening...
175 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
176 return NS_OK;
179 //-----------------------------------------------------------------------------
180 // nsServerSocket::nsASocketHandler
181 //-----------------------------------------------------------------------------
183 void
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;
194 return;
197 PRFileDesc *clientFD;
198 PRNetAddr clientAddr;
200 clientFD = PR_Accept(mFD, &clientAddr, PR_INTERVAL_NO_WAIT);
201 if (!clientFD)
203 NS_WARNING("PR_Accept failed");
204 mCondition = NS_ERROR_UNEXPECTED;
206 else
208 nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
209 if (!trans)
210 mCondition = NS_ERROR_OUT_OF_MEMORY;
211 else
213 nsresult rv = trans->InitWithConnectedSocket(clientFD, &clientAddr);
214 if (NS_FAILED(rv))
215 mCondition = rv;
216 else
217 mListener->OnSocketAccepted(this, trans);
222 void
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;
229 if (mFD)
231 NS_ASSERTION(mFD == fd, "wrong file descriptor");
232 PR_Close(mFD);
233 mFD = nsnull;
236 if (mListener)
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.
248 if (listener)
249 NS_ProxyRelease(mListenerTarget, listener);
254 //-----------------------------------------------------------------------------
255 // nsServerSocket::nsISupports
256 //-----------------------------------------------------------------------------
258 NS_IMPL_THREADSAFE_ISUPPORTS1(nsServerSocket, nsIServerSocket)
261 //-----------------------------------------------------------------------------
262 // nsServerSocket::nsIServerSocket
263 //-----------------------------------------------------------------------------
265 NS_IMETHODIMP
266 nsServerSocket::Init(PRInt32 aPort, PRBool aLoopbackOnly, PRInt32 aBackLog)
268 PRNetAddrValue val;
269 PRNetAddr addr;
271 if (aPort < 0)
272 aPort = 0;
273 if (aLoopbackOnly)
274 val = PR_IpAddrLoopback;
275 else
276 val = PR_IpAddrAny;
277 PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
279 return InitWithAddress(&addr, aBackLog);
282 NS_IMETHODIMP
283 nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, PRInt32 aBackLog)
285 NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
287 if (!mLock)
289 mLock = PR_NewLock();
290 if (!mLock)
291 return NS_ERROR_OUT_OF_MEMORY;
295 // configure listening socket...
298 mFD = PR_OpenTCPSocket(aAddr->raw.family);
299 if (!mFD)
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");
318 goto fail;
321 if (aBackLog < 0)
322 aBackLog = 5; // seems like a reasonable default
324 if (PR_Listen(mFD, aBackLog) != PR_SUCCESS)
326 NS_WARNING("cannot listen on socket");
327 goto fail;
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");
335 goto fail;
338 // wait until AsyncListen is called before polling the socket for
339 // client connections.
340 return NS_OK;
342 fail:
343 Close();
344 return NS_ERROR_FAILURE;
347 NS_IMETHODIMP
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...
355 if (!mListener)
357 if (mFD)
359 PR_Close(mFD);
360 mFD = nsnull;
362 return NS_OK;
365 return PostEvent(this, &nsServerSocket::OnMsgClose);
368 NS_IMETHODIMP
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),
378 aListener,
379 NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
380 getter_AddRefs(mListener));
381 if (NS_FAILED(rv))
382 return rv;
383 mListenerTarget = NS_GetCurrentThread();
385 return PostEvent(this, &nsServerSocket::OnMsgAttach);
388 NS_IMETHODIMP
389 nsServerSocket::GetPort(PRInt32 *aResult)
391 // no need to enter the lock here
392 PRUint16 port;
393 if (mAddr.raw.family == PR_AF_INET)
394 port = mAddr.inet.port;
395 else
396 port = mAddr.ipv6.port;
397 *aResult = (PRInt32) PR_ntohs(port);
398 return NS_OK;
401 NS_IMETHODIMP
402 nsServerSocket::GetAddress(PRNetAddr *aResult)
404 // no need to enter the lock here
405 memcpy(aResult, &mAddr, sizeof(mAddr));
406 return NS_OK;