michael@0: // vim:set sw=4 sts=4 et cin: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG michael@0: #endif michael@0: michael@0: #include "nsSocketTransportService2.h" michael@0: #include "nsSocketTransport2.h" michael@0: #include "nsError.h" michael@0: #include "prnetdb.h" michael@0: #include "prerror.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "NetworkActivityMonitor.h" michael@0: #include "nsIObserverService.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/PublicSSL.h" michael@0: #include "mozilla/ChaosMode.h" michael@0: #include "mozilla/PodOperations.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIFile.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::net; michael@0: michael@0: #if defined(PR_LOGGING) michael@0: PRLogModuleInfo *gSocketTransportLog = nullptr; michael@0: #endif michael@0: michael@0: nsSocketTransportService *gSocketTransportService = nullptr; michael@0: PRThread *gSocketThread = nullptr; michael@0: michael@0: #define SEND_BUFFER_PREF "network.tcp.sendbuffer" michael@0: #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled" michael@0: #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time" michael@0: #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval" michael@0: #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count" michael@0: #define SOCKET_LIMIT_TARGET 550U michael@0: #define SOCKET_LIMIT_MIN 50U michael@0: #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds" michael@0: michael@0: uint32_t nsSocketTransportService::gMaxCount; michael@0: PRCallOnceType nsSocketTransportService::gMaxCountInitOnce; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // ctor/dtor (called on the main/UI thread by the service manager) michael@0: michael@0: nsSocketTransportService::nsSocketTransportService() michael@0: : mThread(nullptr) michael@0: , mThreadEvent(nullptr) michael@0: , mAutodialEnabled(false) michael@0: , mLock("nsSocketTransportService::mLock") michael@0: , mInitialized(false) michael@0: , mShuttingDown(false) michael@0: , mOffline(false) michael@0: , mGoingOffline(false) michael@0: , mActiveListSize(SOCKET_LIMIT_MIN) michael@0: , mIdleListSize(SOCKET_LIMIT_MIN) michael@0: , mActiveCount(0) michael@0: , mIdleCount(0) michael@0: , mSentBytesCount(0) michael@0: , mReceivedBytesCount(0) michael@0: , mSendBufferSize(0) michael@0: , mKeepaliveIdleTimeS(600) michael@0: , mKeepaliveRetryIntervalS(1) michael@0: , mKeepaliveProbeCount(kDefaultTCPKeepCount) michael@0: , mKeepaliveEnabledPref(false) michael@0: , mProbedMaxCount(false) michael@0: { michael@0: #if defined(PR_LOGGING) michael@0: gSocketTransportLog = PR_NewLogModule("nsSocketTransport"); michael@0: #endif michael@0: michael@0: NS_ASSERTION(NS_IsMainThread(), "wrong thread"); michael@0: michael@0: PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount); michael@0: mActiveList = (SocketContext *) michael@0: moz_xmalloc(sizeof(SocketContext) * mActiveListSize); michael@0: mIdleList = (SocketContext *) michael@0: moz_xmalloc(sizeof(SocketContext) * mIdleListSize); michael@0: mPollList = (PRPollDesc *) michael@0: moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1)); michael@0: michael@0: NS_ASSERTION(!gSocketTransportService, "must not instantiate twice"); michael@0: gSocketTransportService = this; michael@0: } michael@0: michael@0: nsSocketTransportService::~nsSocketTransportService() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "wrong thread"); michael@0: NS_ASSERTION(!mInitialized, "not shutdown properly"); michael@0: michael@0: if (mThreadEvent) michael@0: PR_DestroyPollableEvent(mThreadEvent); michael@0: michael@0: moz_free(mActiveList); michael@0: moz_free(mIdleList); michael@0: moz_free(mPollList); michael@0: gSocketTransportService = nullptr; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // event queue (any thread) michael@0: michael@0: already_AddRefed michael@0: nsSocketTransportService::GetThreadSafely() michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: nsCOMPtr result = mThread; michael@0: return result.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::Dispatch(nsIRunnable *event, uint32_t flags) michael@0: { michael@0: SOCKET_LOG(("STS dispatch [%p]\n", event)); michael@0: michael@0: nsCOMPtr thread = GetThreadSafely(); michael@0: nsresult rv; michael@0: rv = thread ? thread->Dispatch(event, flags) : NS_ERROR_NOT_INITIALIZED; michael@0: if (rv == NS_ERROR_UNEXPECTED) { michael@0: // Thread is no longer accepting events. We must have just shut it michael@0: // down on the main thread. Pretend we never saw it. michael@0: rv = NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::IsOnCurrentThread(bool *result) michael@0: { michael@0: nsCOMPtr thread = GetThreadSafely(); michael@0: NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED); michael@0: return thread->IsOnCurrentThread(result); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // socket api (socket thread only) michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event) michael@0: { michael@0: SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n")); michael@0: michael@0: NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); michael@0: michael@0: if (CanAttachSocket()) { michael@0: return Dispatch(event, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: mPendingSocketQ.PutEvent(event); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler) michael@0: { michael@0: SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler)); michael@0: michael@0: NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); michael@0: michael@0: if (!CanAttachSocket()) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: SocketContext sock; michael@0: sock.mFD = fd; michael@0: sock.mHandler = handler; michael@0: sock.mElapsedTime = 0; michael@0: michael@0: nsresult rv = AddToIdleList(&sock); michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_ADDREF(handler); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock) michael@0: { michael@0: SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler)); michael@0: NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList), michael@0: "DetachSocket invalid head"); michael@0: michael@0: // inform the handler that this socket is going away michael@0: sock->mHandler->OnSocketDetached(sock->mFD); michael@0: mSentBytesCount += sock->mHandler->ByteCountSent(); michael@0: mReceivedBytesCount += sock->mHandler->ByteCountReceived(); michael@0: michael@0: // cleanup michael@0: sock->mFD = nullptr; michael@0: NS_RELEASE(sock->mHandler); michael@0: michael@0: if (listHead == mActiveList) michael@0: RemoveFromPollList(sock); michael@0: else michael@0: RemoveFromIdleList(sock); michael@0: michael@0: // NOTE: sock is now an invalid pointer michael@0: michael@0: // michael@0: // notify the first element on the pending socket queue... michael@0: // michael@0: nsCOMPtr event; michael@0: if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) { michael@0: // move event from pending queue to dispatch queue michael@0: return Dispatch(event, NS_DISPATCH_NORMAL); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsSocketTransportService::AddToPollList(SocketContext *sock) michael@0: { michael@0: NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mActiveList)) < mActiveListSize), michael@0: "AddToPollList Socket Already Active"); michael@0: michael@0: SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler)); michael@0: if (mActiveCount == mActiveListSize) { michael@0: SOCKET_LOG((" Active List size of %d met\n", mActiveCount)); michael@0: if (!GrowActiveList()) { michael@0: NS_ERROR("too many active sockets"); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: uint32_t newSocketIndex = mActiveCount; michael@0: if (ChaosMode::isActive()) { michael@0: newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1); michael@0: PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex, michael@0: mActiveCount - newSocketIndex); michael@0: PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1, michael@0: mActiveCount - newSocketIndex); michael@0: } michael@0: mActiveList[newSocketIndex] = *sock; michael@0: mActiveCount++; michael@0: michael@0: mPollList[newSocketIndex + 1].fd = sock->mFD; michael@0: mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags; michael@0: mPollList[newSocketIndex + 1].out_flags = 0; michael@0: michael@0: SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::RemoveFromPollList(SocketContext *sock) michael@0: { michael@0: SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler)); michael@0: michael@0: uint32_t index = sock - mActiveList; michael@0: NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index"); michael@0: michael@0: SOCKET_LOG((" index=%u mActiveCount=%u\n", index, mActiveCount)); michael@0: michael@0: if (index != mActiveCount-1) { michael@0: mActiveList[index] = mActiveList[mActiveCount-1]; michael@0: mPollList[index+1] = mPollList[mActiveCount]; michael@0: } michael@0: mActiveCount--; michael@0: michael@0: SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); michael@0: } michael@0: michael@0: nsresult michael@0: nsSocketTransportService::AddToIdleList(SocketContext *sock) michael@0: { michael@0: NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mIdleList)) < mIdleListSize), michael@0: "AddToIdlelList Socket Already Idle"); michael@0: michael@0: SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler)); michael@0: if (mIdleCount == mIdleListSize) { michael@0: SOCKET_LOG((" Idle List size of %d met\n", mIdleCount)); michael@0: if (!GrowIdleList()) { michael@0: NS_ERROR("too many idle sockets"); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: mIdleList[mIdleCount] = *sock; michael@0: mIdleCount++; michael@0: michael@0: SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::RemoveFromIdleList(SocketContext *sock) michael@0: { michael@0: SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler)); michael@0: michael@0: uint32_t index = sock - mIdleList; michael@0: NS_ASSERTION(index < mIdleListSize, "invalid index in idle list"); michael@0: michael@0: if (index != mIdleCount-1) michael@0: mIdleList[index] = mIdleList[mIdleCount-1]; michael@0: mIdleCount--; michael@0: michael@0: SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::MoveToIdleList(SocketContext *sock) michael@0: { michael@0: nsresult rv = AddToIdleList(sock); michael@0: if (NS_FAILED(rv)) michael@0: DetachSocket(mActiveList, sock); michael@0: else michael@0: RemoveFromPollList(sock); michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::MoveToPollList(SocketContext *sock) michael@0: { michael@0: nsresult rv = AddToPollList(sock); michael@0: if (NS_FAILED(rv)) michael@0: DetachSocket(mIdleList, sock); michael@0: else michael@0: RemoveFromIdleList(sock); michael@0: } michael@0: michael@0: bool michael@0: nsSocketTransportService::GrowActiveList() michael@0: { michael@0: int32_t toAdd = gMaxCount - mActiveListSize; michael@0: if (toAdd > 100) michael@0: toAdd = 100; michael@0: if (toAdd < 1) michael@0: return false; michael@0: michael@0: mActiveListSize += toAdd; michael@0: mActiveList = (SocketContext *) michael@0: moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize); michael@0: mPollList = (PRPollDesc *) michael@0: moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsSocketTransportService::GrowIdleList() michael@0: { michael@0: int32_t toAdd = gMaxCount - mIdleListSize; michael@0: if (toAdd > 100) michael@0: toAdd = 100; michael@0: if (toAdd < 1) michael@0: return false; michael@0: michael@0: mIdleListSize += toAdd; michael@0: mIdleList = (SocketContext *) michael@0: moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize); michael@0: return true; michael@0: } michael@0: michael@0: PRIntervalTime michael@0: nsSocketTransportService::PollTimeout() michael@0: { michael@0: if (mActiveCount == 0) michael@0: return NS_SOCKET_POLL_TIMEOUT; michael@0: michael@0: // compute minimum time before any socket timeout expires. michael@0: uint32_t minR = UINT16_MAX; michael@0: for (uint32_t i=0; imPollTimeout) michael@0: ? s.mHandler->mPollTimeout - s.mElapsedTime michael@0: : 0; michael@0: if (r < minR) michael@0: minR = r; michael@0: } michael@0: // nsASocketHandler defines UINT16_MAX as do not timeout michael@0: if (minR == UINT16_MAX) { michael@0: SOCKET_LOG(("poll timeout: none\n")); michael@0: return NS_SOCKET_POLL_TIMEOUT; michael@0: } michael@0: SOCKET_LOG(("poll timeout: %lu\n", minR)); michael@0: return PR_SecondsToInterval(minR); michael@0: } michael@0: michael@0: int32_t michael@0: nsSocketTransportService::Poll(bool wait, uint32_t *interval) michael@0: { michael@0: PRPollDesc *pollList; michael@0: uint32_t pollCount; michael@0: PRIntervalTime pollTimeout; michael@0: michael@0: if (mPollList[0].fd) { michael@0: mPollList[0].out_flags = 0; michael@0: pollList = mPollList; michael@0: pollCount = mActiveCount + 1; michael@0: pollTimeout = PollTimeout(); michael@0: } michael@0: else { michael@0: // no pollable event, so busy wait... michael@0: pollCount = mActiveCount; michael@0: if (pollCount) michael@0: pollList = &mPollList[1]; michael@0: else michael@0: pollList = nullptr; michael@0: pollTimeout = PR_MillisecondsToInterval(25); michael@0: } michael@0: michael@0: if (!wait) michael@0: pollTimeout = PR_INTERVAL_NO_WAIT; michael@0: michael@0: PRIntervalTime ts = PR_IntervalNow(); michael@0: michael@0: SOCKET_LOG((" timeout = %i milliseconds\n", michael@0: PR_IntervalToMilliseconds(pollTimeout))); michael@0: int32_t rv = PR_Poll(pollList, pollCount, pollTimeout); michael@0: michael@0: PRIntervalTime passedInterval = PR_IntervalNow() - ts; michael@0: michael@0: SOCKET_LOG((" ...returned after %i milliseconds\n", michael@0: PR_IntervalToMilliseconds(passedInterval))); michael@0: michael@0: *interval = PR_IntervalToSeconds(passedInterval); michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // xpcom api michael@0: michael@0: NS_IMPL_ISUPPORTS(nsSocketTransportService, michael@0: nsISocketTransportService, michael@0: nsIEventTarget, michael@0: nsIThreadObserver, michael@0: nsIRunnable, michael@0: nsPISocketTransportService, michael@0: nsIObserver) michael@0: michael@0: // called from main thread only michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::Init() michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("wrong thread"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (mInitialized) michael@0: return NS_OK; michael@0: michael@0: if (mShuttingDown) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if (!mThreadEvent) { michael@0: mThreadEvent = PR_NewPollableEvent(); michael@0: // michael@0: // NOTE: per bug 190000, this failure could be caused by Zone-Alarm michael@0: // or similar software. michael@0: // michael@0: // NOTE: per bug 191739, this failure could also be caused by lack michael@0: // of a loopback device on Windows and OS/2 platforms (NSPR creates michael@0: // a loopback socket pair on these platforms to implement a pollable michael@0: // event object). if we can't create a pollable event, then we'll michael@0: // have to "busy wait" to implement the socket event queue :-( michael@0: // michael@0: if (!mThreadEvent) { michael@0: NS_WARNING("running socket transport thread without a pollable event"); michael@0: SOCKET_LOG(("running socket transport thread without a pollable event")); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr thread; michael@0: nsresult rv = NS_NewThread(getter_AddRefs(thread), this); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: // Install our mThread, protecting against concurrent readers michael@0: thread.swap(mThread); michael@0: } michael@0: michael@0: nsCOMPtr tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (tmpPrefService) { michael@0: tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false); michael@0: tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false); michael@0: tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false); michael@0: tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false); michael@0: tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false); michael@0: } michael@0: UpdatePrefs(); michael@0: michael@0: nsCOMPtr obsSvc = services::GetObserverService(); michael@0: if (obsSvc) { michael@0: obsSvc->AddObserver(this, "profile-initial-state", false); michael@0: obsSvc->AddObserver(this, "last-pb-context-exited", false); michael@0: } michael@0: michael@0: mInitialized = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // called from main thread only michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::Shutdown() michael@0: { michael@0: SOCKET_LOG(("nsSocketTransportService::Shutdown\n")); michael@0: michael@0: NS_ENSURE_STATE(NS_IsMainThread()); michael@0: michael@0: if (!mInitialized) michael@0: return NS_OK; michael@0: michael@0: if (mShuttingDown) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: michael@0: // signal the socket thread to shutdown michael@0: mShuttingDown = true; michael@0: michael@0: if (mThreadEvent) michael@0: PR_SetPollableEvent(mThreadEvent); michael@0: // else wait for Poll timeout michael@0: } michael@0: michael@0: // join with thread michael@0: mThread->Shutdown(); michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: // Drop our reference to mThread and make sure that any concurrent michael@0: // readers are excluded michael@0: mThread = nullptr; michael@0: } michael@0: michael@0: nsCOMPtr tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (tmpPrefService) michael@0: tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this); michael@0: michael@0: nsCOMPtr obsSvc = services::GetObserverService(); michael@0: if (obsSvc) { michael@0: obsSvc->RemoveObserver(this, "profile-initial-state"); michael@0: obsSvc->RemoveObserver(this, "last-pb-context-exited"); michael@0: } michael@0: michael@0: mozilla::net::NetworkActivityMonitor::Shutdown(); michael@0: michael@0: mInitialized = false; michael@0: mShuttingDown = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::GetOffline(bool *offline) michael@0: { michael@0: *offline = mOffline; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::SetOffline(bool offline) michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: if (!mOffline && offline) { michael@0: // signal the socket thread to go offline, so it will detach sockets michael@0: mGoingOffline = true; michael@0: mOffline = true; michael@0: } michael@0: else if (mOffline && !offline) { michael@0: mOffline = false; michael@0: } michael@0: if (mThreadEvent) michael@0: PR_SetPollableEvent(mThreadEvent); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS) michael@0: { michael@0: MOZ_ASSERT(aKeepaliveIdleTimeS); michael@0: if (NS_WARN_IF(!aKeepaliveIdleTimeS)) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS) michael@0: { michael@0: MOZ_ASSERT(aKeepaliveRetryIntervalS); michael@0: if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount) michael@0: { michael@0: MOZ_ASSERT(aKeepaliveProbeCount); michael@0: if (NS_WARN_IF(!aKeepaliveProbeCount)) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: *aKeepaliveProbeCount = mKeepaliveProbeCount; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::CreateTransport(const char **types, michael@0: uint32_t typeCount, michael@0: const nsACString &host, michael@0: int32_t port, michael@0: nsIProxyInfo *proxyInfo, michael@0: nsISocketTransport **result) michael@0: { michael@0: NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE); michael@0: michael@0: nsRefPtr trans = new nsSocketTransport(); michael@0: nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: trans.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath, michael@0: nsISocketTransport **result) michael@0: { michael@0: nsresult rv; michael@0: michael@0: NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsAutoCString path; michael@0: rv = aPath->GetNativePath(path); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsRefPtr trans = new nsSocketTransport(); michael@0: michael@0: rv = trans->InitWithFilename(path.get()); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: trans.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::GetAutodialEnabled(bool *value) michael@0: { michael@0: *value = mAutodialEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::SetAutodialEnabled(bool value) michael@0: { michael@0: mAutodialEnabled = value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread) michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: if (mThreadEvent) michael@0: PR_SetPollableEvent(mThreadEvent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread, michael@0: bool mayWait, uint32_t depth) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread, michael@0: uint32_t depth, michael@0: bool eventWasProcessed) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: #include "ipc/Nuwa.h" michael@0: #endif michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::Run() michael@0: { michael@0: PR_SetCurrentThreadName("Socket Thread"); michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (IsNuwaProcess()) { michael@0: NS_ASSERTION(NuwaMarkCurrentThread != nullptr, michael@0: "NuwaMarkCurrentThread is undefined!"); michael@0: NuwaMarkCurrentThread(nullptr, nullptr); michael@0: } michael@0: #endif michael@0: michael@0: SOCKET_LOG(("STS thread init\n")); michael@0: michael@0: psm::InitializeSSLServerCertVerificationThreads(); michael@0: michael@0: gSocketThread = PR_GetCurrentThread(); michael@0: michael@0: // add thread event to poll list (mThreadEvent may be nullptr) michael@0: mPollList[0].fd = mThreadEvent; michael@0: mPollList[0].in_flags = PR_POLL_READ; michael@0: mPollList[0].out_flags = 0; michael@0: michael@0: nsIThread *thread = NS_GetCurrentThread(); michael@0: michael@0: // hook ourselves up to observe event processing for this thread michael@0: nsCOMPtr threadInt = do_QueryInterface(thread); michael@0: threadInt->SetObserver(this); michael@0: michael@0: // make sure the pseudo random number generator is seeded on this thread michael@0: srand(static_cast(PR_Now())); michael@0: michael@0: for (;;) { michael@0: bool pendingEvents = false; michael@0: thread->HasPendingEvents(&pendingEvents); michael@0: michael@0: do { michael@0: // If there are pending events for this thread then michael@0: // DoPollIteration() should service the network without blocking. michael@0: DoPollIteration(!pendingEvents); michael@0: michael@0: // If nothing was pending before the poll, it might be now michael@0: if (!pendingEvents) michael@0: thread->HasPendingEvents(&pendingEvents); michael@0: michael@0: if (pendingEvents) { michael@0: NS_ProcessNextEvent(thread); michael@0: pendingEvents = false; michael@0: thread->HasPendingEvents(&pendingEvents); michael@0: } michael@0: } while (pendingEvents); michael@0: michael@0: bool goingOffline = false; michael@0: // now that our event queue is empty, check to see if we should exit michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: if (mShuttingDown) michael@0: break; michael@0: if (mGoingOffline) { michael@0: mGoingOffline = false; michael@0: goingOffline = true; michael@0: } michael@0: } michael@0: // Avoid potential deadlock michael@0: if (goingOffline) michael@0: Reset(true); michael@0: } michael@0: michael@0: SOCKET_LOG(("STS shutting down thread\n")); michael@0: michael@0: // detach all sockets, including locals michael@0: Reset(false); michael@0: michael@0: // Final pass over the event queue. This makes sure that events posted by michael@0: // socket detach handlers get processed. michael@0: NS_ProcessPendingEvents(thread); michael@0: michael@0: gSocketThread = nullptr; michael@0: michael@0: psm::StopSSLServerCertVerificationThreads(); michael@0: michael@0: SOCKET_LOG(("STS thread exit\n")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals, michael@0: SocketContext *socketList, michael@0: int32_t index) michael@0: { michael@0: bool isGuarded = false; michael@0: if (aGuardLocals) { michael@0: socketList[index].mHandler->IsLocal(&isGuarded); michael@0: if (!isGuarded) michael@0: socketList[index].mHandler->KeepWhenOffline(&isGuarded); michael@0: } michael@0: if (!isGuarded) michael@0: DetachSocket(socketList, &socketList[index]); michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::Reset(bool aGuardLocals) michael@0: { michael@0: // detach any sockets michael@0: int32_t i; michael@0: for (i = mActiveCount - 1; i >= 0; --i) { michael@0: DetachSocketWithGuard(aGuardLocals, mActiveList, i); michael@0: } michael@0: for (i = mIdleCount - 1; i >= 0; --i) { michael@0: DetachSocketWithGuard(aGuardLocals, mIdleList, i); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsSocketTransportService::DoPollIteration(bool wait) michael@0: { michael@0: SOCKET_LOG(("STS poll iter [%d]\n", wait)); michael@0: michael@0: int32_t i, count; michael@0: michael@0: // michael@0: // poll loop michael@0: // michael@0: // walk active list backwards to see if any sockets should actually be michael@0: // idle, then walk the idle list backwards to see if any idle sockets michael@0: // should become active. take care to check only idle sockets that michael@0: // were idle to begin with ;-) michael@0: // michael@0: count = mIdleCount; michael@0: for (i=mActiveCount-1; i>=0; --i) { michael@0: //--- michael@0: SOCKET_LOG((" active [%u] { handler=%p condition=%x pollflags=%hu }\n", i, michael@0: mActiveList[i].mHandler, michael@0: mActiveList[i].mHandler->mCondition, michael@0: mActiveList[i].mHandler->mPollFlags)); michael@0: //--- michael@0: if (NS_FAILED(mActiveList[i].mHandler->mCondition)) michael@0: DetachSocket(mActiveList, &mActiveList[i]); michael@0: else { michael@0: uint16_t in_flags = mActiveList[i].mHandler->mPollFlags; michael@0: if (in_flags == 0) michael@0: MoveToIdleList(&mActiveList[i]); michael@0: else { michael@0: // update poll flags michael@0: mPollList[i+1].in_flags = in_flags; michael@0: mPollList[i+1].out_flags = 0; michael@0: } michael@0: } michael@0: } michael@0: for (i=count-1; i>=0; --i) { michael@0: //--- michael@0: SOCKET_LOG((" idle [%u] { handler=%p condition=%x pollflags=%hu }\n", i, michael@0: mIdleList[i].mHandler, michael@0: mIdleList[i].mHandler->mCondition, michael@0: mIdleList[i].mHandler->mPollFlags)); michael@0: //--- michael@0: if (NS_FAILED(mIdleList[i].mHandler->mCondition)) michael@0: DetachSocket(mIdleList, &mIdleList[i]); michael@0: else if (mIdleList[i].mHandler->mPollFlags != 0) michael@0: MoveToPollList(&mIdleList[i]); michael@0: } michael@0: michael@0: SOCKET_LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount)); michael@0: michael@0: #if defined(XP_WIN) michael@0: // 30 active connections is the historic limit before firefox 7's 256. A few michael@0: // windows systems have troubles with the higher limit, so actively probe a michael@0: // limit the first time we exceed 30. michael@0: if ((mActiveCount > 30) && !mProbedMaxCount) michael@0: ProbeMaxCount(); michael@0: #endif michael@0: michael@0: // Measures seconds spent while blocked on PR_Poll michael@0: uint32_t pollInterval; michael@0: michael@0: int32_t n = Poll(wait, &pollInterval); michael@0: if (n < 0) { michael@0: SOCKET_LOG((" PR_Poll error [%d]\n", PR_GetError())); michael@0: } michael@0: else { michael@0: // michael@0: // service "active" sockets... michael@0: // michael@0: for (i=0; i 0 && desc.out_flags != 0) { michael@0: s.mElapsedTime = 0; michael@0: s.mHandler->OnSocketReady(desc.fd, desc.out_flags); michael@0: } michael@0: // check for timeout errors unless disabled... michael@0: else if (s.mHandler->mPollTimeout != UINT16_MAX) { michael@0: // update elapsed time counter michael@0: // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value michael@0: // here -- otherwise, some compilers will treat it as signed, michael@0: // which makes them fire signed/unsigned-comparison build michael@0: // warnings for the comparison against 'pollInterval'.) michael@0: if (MOZ_UNLIKELY(pollInterval > michael@0: static_cast(UINT16_MAX) - michael@0: s.mElapsedTime)) michael@0: s.mElapsedTime = UINT16_MAX; michael@0: else michael@0: s.mElapsedTime += uint16_t(pollInterval); michael@0: // check for timeout expiration michael@0: if (s.mElapsedTime >= s.mHandler->mPollTimeout) { michael@0: s.mElapsedTime = 0; michael@0: s.mHandler->OnSocketReady(desc.fd, -1); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // michael@0: // check for "dead" sockets and remove them (need to do this in michael@0: // reverse order obviously). michael@0: // michael@0: for (i=mActiveCount-1; i>=0; --i) { michael@0: if (NS_FAILED(mActiveList[i].mHandler->mCondition)) michael@0: DetachSocket(mActiveList, &mActiveList[i]); michael@0: } michael@0: michael@0: if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) { michael@0: // acknowledge pollable event (wait should not block) michael@0: if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) { michael@0: // On Windows, the TCP loopback connection in the michael@0: // pollable event may become broken when a laptop michael@0: // switches between wired and wireless networks or michael@0: // wakes up from hibernation. We try to create a michael@0: // new pollable event. If that fails, we fall back michael@0: // on "busy wait". michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: PR_DestroyPollableEvent(mThreadEvent); michael@0: mThreadEvent = PR_NewPollableEvent(); michael@0: } michael@0: if (!mThreadEvent) { michael@0: NS_WARNING("running socket transport thread without " michael@0: "a pollable event"); michael@0: SOCKET_LOG(("running socket transport thread without " michael@0: "a pollable event")); michael@0: } michael@0: mPollList[0].fd = mThreadEvent; michael@0: // mPollList[0].in_flags was already set to PR_POLL_READ michael@0: // in Run(). michael@0: mPollList[0].out_flags = 0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsSocketTransportService::UpdatePrefs() michael@0: { michael@0: mSendBufferSize = 0; michael@0: michael@0: nsCOMPtr tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (tmpPrefService) { michael@0: int32_t bufferSize; michael@0: nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize); michael@0: if (NS_SUCCEEDED(rv) && bufferSize > 0) michael@0: mSendBufferSize = bufferSize; michael@0: michael@0: // Default TCP Keepalive Values. michael@0: int32_t keepaliveIdleTimeS; michael@0: rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF, michael@0: &keepaliveIdleTimeS); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS, michael@0: 1, kMaxTCPKeepIdle); michael@0: michael@0: int32_t keepaliveRetryIntervalS; michael@0: rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF, michael@0: &keepaliveRetryIntervalS); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS, michael@0: 1, kMaxTCPKeepIntvl); michael@0: michael@0: int32_t keepaliveProbeCount; michael@0: rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF, michael@0: &keepaliveProbeCount); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mKeepaliveProbeCount = clamped(keepaliveProbeCount, michael@0: 1, kMaxTCPKeepCount); michael@0: bool keepaliveEnabled = false; michael@0: rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF, michael@0: &keepaliveEnabled); michael@0: if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) { michael@0: mKeepaliveEnabledPref = keepaliveEnabled; michael@0: OnKeepaliveEnabledPrefChange(); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::OnKeepaliveEnabledPrefChange() michael@0: { michael@0: // Dispatch to socket thread if we're not executing there. michael@0: if (PR_GetCurrentThread() != gSocketThread) { michael@0: gSocketTransportService->Dispatch( michael@0: NS_NewRunnableMethod( michael@0: this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange), michael@0: NS_DISPATCH_NORMAL); michael@0: return; michael@0: } michael@0: michael@0: SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s", michael@0: mKeepaliveEnabledPref ? "enabled" : "disabled")); michael@0: michael@0: // Notify each socket that keepalive has been en/disabled globally. michael@0: for (int32_t i = mActiveCount - 1; i >= 0; --i) { michael@0: NotifyKeepaliveEnabledPrefChange(&mActiveList[i]); michael@0: } michael@0: for (int32_t i = mIdleCount - 1; i >= 0; --i) { michael@0: NotifyKeepaliveEnabledPrefChange(&mIdleList[i]); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock) michael@0: { michael@0: MOZ_ASSERT(sock, "SocketContext cannot be null!"); michael@0: MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!"); michael@0: michael@0: if (!sock || !sock->mHandler) { michael@0: return; michael@0: } michael@0: michael@0: sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::Observe(nsISupports *subject, michael@0: const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { michael@0: UpdatePrefs(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp(topic, "profile-initial-state")) { michael@0: int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0); michael@0: if (blipInterval <= 0) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return net::NetworkActivityMonitor::Init(blipInterval); michael@0: } michael@0: michael@0: if (!strcmp(topic, "last-pb-context-exited")) { michael@0: nsCOMPtr ev = michael@0: NS_NewRunnableMethod(this, michael@0: &nsSocketTransportService::ClosePrivateConnections); michael@0: nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::ClosePrivateConnections() michael@0: { michael@0: // Must be called on the socket thread. michael@0: #ifdef DEBUG michael@0: bool onSTSThread; michael@0: IsOnCurrentThread(&onSTSThread); michael@0: MOZ_ASSERT(onSTSThread); michael@0: #endif michael@0: michael@0: for (int32_t i = mActiveCount - 1; i >= 0; --i) { michael@0: if (mActiveList[i].mHandler->mIsPrivate) { michael@0: DetachSocket(mActiveList, &mActiveList[i]); michael@0: } michael@0: } michael@0: for (int32_t i = mIdleCount - 1; i >= 0; --i) { michael@0: if (mIdleList[i].mHandler->mIsPrivate) { michael@0: DetachSocket(mIdleList, &mIdleList[i]); michael@0: } michael@0: } michael@0: michael@0: mozilla::ClearPrivateSSLState(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSocketTransportService::GetSendBufferSize(int32_t *value) michael@0: { michael@0: *value = mSendBufferSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /// ugly OS specific includes are placed at the bottom of the src for clarity michael@0: michael@0: #if defined(XP_WIN) michael@0: #include michael@0: #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX) michael@0: #include michael@0: #endif michael@0: michael@0: // Right now the only need to do this is on windows. michael@0: #if defined(XP_WIN) michael@0: void michael@0: nsSocketTransportService::ProbeMaxCount() michael@0: { michael@0: NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); michael@0: michael@0: if (mProbedMaxCount) michael@0: return; michael@0: mProbedMaxCount = true; michael@0: michael@0: // Allocate and test a PR_Poll up to the gMaxCount number of unconnected michael@0: // sockets. See bug 692260 - windows should be able to handle 1000 sockets michael@0: // in select() without a problem, but LSPs have been known to balk at lower michael@0: // numbers. (64 in the bug). michael@0: michael@0: // Allocate michael@0: struct PRPollDesc pfd[SOCKET_LIMIT_TARGET]; michael@0: uint32_t numAllocated = 0; michael@0: michael@0: for (uint32_t index = 0 ; index < gMaxCount; ++index) { michael@0: pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT; michael@0: pfd[index].out_flags = 0; michael@0: pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET); michael@0: if (!pfd[index].fd) { michael@0: SOCKET_LOG(("Socket Limit Test index %d failed\n", index)); michael@0: if (index < SOCKET_LIMIT_MIN) michael@0: gMaxCount = SOCKET_LIMIT_MIN; michael@0: else michael@0: gMaxCount = index; michael@0: break; michael@0: } michael@0: ++numAllocated; michael@0: } michael@0: michael@0: // Test michael@0: PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U); michael@0: while (gMaxCount <= numAllocated) { michael@0: int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0)); michael@0: michael@0: SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n", michael@0: gMaxCount, rv)); michael@0: michael@0: if (rv >= 0) michael@0: break; michael@0: michael@0: SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n", michael@0: gMaxCount, rv, PR_GetError())); michael@0: michael@0: gMaxCount -= 32; michael@0: if (gMaxCount <= SOCKET_LIMIT_MIN) { michael@0: gMaxCount = SOCKET_LIMIT_MIN; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Free michael@0: for (uint32_t index = 0 ; index < numAllocated; ++index) michael@0: if (pfd[index].fd) michael@0: PR_Close(pfd[index].fd); michael@0: michael@0: SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount)); michael@0: } michael@0: #endif // windows michael@0: michael@0: PRStatus michael@0: nsSocketTransportService::DiscoverMaxCount() michael@0: { michael@0: gMaxCount = SOCKET_LIMIT_MIN; michael@0: michael@0: #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX) michael@0: // On unix and os x network sockets and file michael@0: // descriptors are the same. OS X comes defaulted at 256, michael@0: // most linux at 1000. We can reliably use [sg]rlimit to michael@0: // query that and raise it. We will try to raise it 250 past michael@0: // our target number of SOCKET_LIMIT_TARGET so that some descriptors michael@0: // are still available for other things. michael@0: michael@0: struct rlimit rlimitData; michael@0: if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) michael@0: return PR_SUCCESS; michael@0: if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET + 250) { michael@0: gMaxCount = SOCKET_LIMIT_TARGET; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: int32_t maxallowed = rlimitData.rlim_max; michael@0: if (maxallowed == -1) { /* no limit */ michael@0: maxallowed = SOCKET_LIMIT_TARGET + 250; michael@0: } else if ((uint32_t)maxallowed < SOCKET_LIMIT_MIN + 250) { michael@0: return PR_SUCCESS; michael@0: } else if ((uint32_t)maxallowed > SOCKET_LIMIT_TARGET + 250) { michael@0: maxallowed = SOCKET_LIMIT_TARGET + 250; michael@0: } michael@0: michael@0: rlimitData.rlim_cur = maxallowed; michael@0: setrlimit(RLIMIT_NOFILE, &rlimitData); michael@0: if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) michael@0: if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250) michael@0: gMaxCount = rlimitData.rlim_cur - 250; michael@0: michael@0: #elif defined(XP_WIN) && !defined(WIN_CE) michael@0: // >= XP is confirmed to have at least 1000 michael@0: gMaxCount = SOCKET_LIMIT_TARGET; michael@0: #else michael@0: // other platforms are harder to test - so leave at safe legacy value michael@0: #endif michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: michael@0: // Used to return connection info to Dashboard.cpp michael@0: void michael@0: nsSocketTransportService::AnalyzeConnection(nsTArray *data, michael@0: struct SocketContext *context, bool aActive) michael@0: { michael@0: if (context->mHandler->mIsPrivate) michael@0: return; michael@0: PRFileDesc *aFD = context->mFD; michael@0: bool tcp = (PR_GetDescType(aFD) == PR_DESC_SOCKET_TCP); michael@0: michael@0: PRNetAddr peer_addr; michael@0: PR_GetPeerName(aFD, &peer_addr); michael@0: michael@0: char host[64] = {0}; michael@0: PR_NetAddrToString(&peer_addr, host, sizeof(host)); michael@0: michael@0: uint16_t port; michael@0: if (peer_addr.raw.family == PR_AF_INET) michael@0: port = peer_addr.inet.port; michael@0: else michael@0: port = peer_addr.ipv6.port; michael@0: port = PR_ntohs(port); michael@0: uint64_t sent = context->mHandler->ByteCountSent(); michael@0: uint64_t received = context->mHandler->ByteCountReceived(); michael@0: SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp }; michael@0: michael@0: data->AppendElement(info); michael@0: } michael@0: michael@0: void michael@0: nsSocketTransportService::GetSocketConnections(nsTArray *data) michael@0: { michael@0: NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); michael@0: for (uint32_t i = 0; i < mActiveCount; i++) michael@0: AnalyzeConnection(data, &mActiveList[i], true); michael@0: for (uint32_t i = 0; i < mIdleCount; i++) michael@0: AnalyzeConnection(data, &mIdleList[i], false); michael@0: }