1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsSocketTransportService2.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1250 @@ 1.4 +// vim:set sw=4 sts=4 et cin: 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifdef MOZ_LOGGING 1.10 +#define FORCE_PR_LOG 1.11 +#endif 1.12 + 1.13 +#include "nsSocketTransportService2.h" 1.14 +#include "nsSocketTransport2.h" 1.15 +#include "nsError.h" 1.16 +#include "prnetdb.h" 1.17 +#include "prerror.h" 1.18 +#include "nsIPrefService.h" 1.19 +#include "nsIPrefBranch.h" 1.20 +#include "nsServiceManagerUtils.h" 1.21 +#include "NetworkActivityMonitor.h" 1.22 +#include "nsIObserverService.h" 1.23 +#include "mozilla/Services.h" 1.24 +#include "mozilla/Preferences.h" 1.25 +#include "mozilla/Likely.h" 1.26 +#include "mozilla/PublicSSL.h" 1.27 +#include "mozilla/ChaosMode.h" 1.28 +#include "mozilla/PodOperations.h" 1.29 +#include "nsThreadUtils.h" 1.30 +#include "nsIFile.h" 1.31 + 1.32 +using namespace mozilla; 1.33 +using namespace mozilla::net; 1.34 + 1.35 +#if defined(PR_LOGGING) 1.36 +PRLogModuleInfo *gSocketTransportLog = nullptr; 1.37 +#endif 1.38 + 1.39 +nsSocketTransportService *gSocketTransportService = nullptr; 1.40 +PRThread *gSocketThread = nullptr; 1.41 + 1.42 +#define SEND_BUFFER_PREF "network.tcp.sendbuffer" 1.43 +#define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled" 1.44 +#define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time" 1.45 +#define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval" 1.46 +#define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count" 1.47 +#define SOCKET_LIMIT_TARGET 550U 1.48 +#define SOCKET_LIMIT_MIN 50U 1.49 +#define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds" 1.50 + 1.51 +uint32_t nsSocketTransportService::gMaxCount; 1.52 +PRCallOnceType nsSocketTransportService::gMaxCountInitOnce; 1.53 + 1.54 +//----------------------------------------------------------------------------- 1.55 +// ctor/dtor (called on the main/UI thread by the service manager) 1.56 + 1.57 +nsSocketTransportService::nsSocketTransportService() 1.58 + : mThread(nullptr) 1.59 + , mThreadEvent(nullptr) 1.60 + , mAutodialEnabled(false) 1.61 + , mLock("nsSocketTransportService::mLock") 1.62 + , mInitialized(false) 1.63 + , mShuttingDown(false) 1.64 + , mOffline(false) 1.65 + , mGoingOffline(false) 1.66 + , mActiveListSize(SOCKET_LIMIT_MIN) 1.67 + , mIdleListSize(SOCKET_LIMIT_MIN) 1.68 + , mActiveCount(0) 1.69 + , mIdleCount(0) 1.70 + , mSentBytesCount(0) 1.71 + , mReceivedBytesCount(0) 1.72 + , mSendBufferSize(0) 1.73 + , mKeepaliveIdleTimeS(600) 1.74 + , mKeepaliveRetryIntervalS(1) 1.75 + , mKeepaliveProbeCount(kDefaultTCPKeepCount) 1.76 + , mKeepaliveEnabledPref(false) 1.77 + , mProbedMaxCount(false) 1.78 +{ 1.79 +#if defined(PR_LOGGING) 1.80 + gSocketTransportLog = PR_NewLogModule("nsSocketTransport"); 1.81 +#endif 1.82 + 1.83 + NS_ASSERTION(NS_IsMainThread(), "wrong thread"); 1.84 + 1.85 + PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount); 1.86 + mActiveList = (SocketContext *) 1.87 + moz_xmalloc(sizeof(SocketContext) * mActiveListSize); 1.88 + mIdleList = (SocketContext *) 1.89 + moz_xmalloc(sizeof(SocketContext) * mIdleListSize); 1.90 + mPollList = (PRPollDesc *) 1.91 + moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1)); 1.92 + 1.93 + NS_ASSERTION(!gSocketTransportService, "must not instantiate twice"); 1.94 + gSocketTransportService = this; 1.95 +} 1.96 + 1.97 +nsSocketTransportService::~nsSocketTransportService() 1.98 +{ 1.99 + NS_ASSERTION(NS_IsMainThread(), "wrong thread"); 1.100 + NS_ASSERTION(!mInitialized, "not shutdown properly"); 1.101 + 1.102 + if (mThreadEvent) 1.103 + PR_DestroyPollableEvent(mThreadEvent); 1.104 + 1.105 + moz_free(mActiveList); 1.106 + moz_free(mIdleList); 1.107 + moz_free(mPollList); 1.108 + gSocketTransportService = nullptr; 1.109 +} 1.110 + 1.111 +//----------------------------------------------------------------------------- 1.112 +// event queue (any thread) 1.113 + 1.114 +already_AddRefed<nsIThread> 1.115 +nsSocketTransportService::GetThreadSafely() 1.116 +{ 1.117 + MutexAutoLock lock(mLock); 1.118 + nsCOMPtr<nsIThread> result = mThread; 1.119 + return result.forget(); 1.120 +} 1.121 + 1.122 +NS_IMETHODIMP 1.123 +nsSocketTransportService::Dispatch(nsIRunnable *event, uint32_t flags) 1.124 +{ 1.125 + SOCKET_LOG(("STS dispatch [%p]\n", event)); 1.126 + 1.127 + nsCOMPtr<nsIThread> thread = GetThreadSafely(); 1.128 + nsresult rv; 1.129 + rv = thread ? thread->Dispatch(event, flags) : NS_ERROR_NOT_INITIALIZED; 1.130 + if (rv == NS_ERROR_UNEXPECTED) { 1.131 + // Thread is no longer accepting events. We must have just shut it 1.132 + // down on the main thread. Pretend we never saw it. 1.133 + rv = NS_ERROR_NOT_INITIALIZED; 1.134 + } 1.135 + return rv; 1.136 +} 1.137 + 1.138 +NS_IMETHODIMP 1.139 +nsSocketTransportService::IsOnCurrentThread(bool *result) 1.140 +{ 1.141 + nsCOMPtr<nsIThread> thread = GetThreadSafely(); 1.142 + NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED); 1.143 + return thread->IsOnCurrentThread(result); 1.144 +} 1.145 + 1.146 +//----------------------------------------------------------------------------- 1.147 +// socket api (socket thread only) 1.148 + 1.149 +NS_IMETHODIMP 1.150 +nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event) 1.151 +{ 1.152 + SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n")); 1.153 + 1.154 + NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); 1.155 + 1.156 + if (CanAttachSocket()) { 1.157 + return Dispatch(event, NS_DISPATCH_NORMAL); 1.158 + } 1.159 + 1.160 + mPendingSocketQ.PutEvent(event); 1.161 + return NS_OK; 1.162 +} 1.163 + 1.164 +NS_IMETHODIMP 1.165 +nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler) 1.166 +{ 1.167 + SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler)); 1.168 + 1.169 + NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); 1.170 + 1.171 + if (!CanAttachSocket()) { 1.172 + return NS_ERROR_NOT_AVAILABLE; 1.173 + } 1.174 + 1.175 + SocketContext sock; 1.176 + sock.mFD = fd; 1.177 + sock.mHandler = handler; 1.178 + sock.mElapsedTime = 0; 1.179 + 1.180 + nsresult rv = AddToIdleList(&sock); 1.181 + if (NS_SUCCEEDED(rv)) 1.182 + NS_ADDREF(handler); 1.183 + return rv; 1.184 +} 1.185 + 1.186 +nsresult 1.187 +nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock) 1.188 +{ 1.189 + SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler)); 1.190 + NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList), 1.191 + "DetachSocket invalid head"); 1.192 + 1.193 + // inform the handler that this socket is going away 1.194 + sock->mHandler->OnSocketDetached(sock->mFD); 1.195 + mSentBytesCount += sock->mHandler->ByteCountSent(); 1.196 + mReceivedBytesCount += sock->mHandler->ByteCountReceived(); 1.197 + 1.198 + // cleanup 1.199 + sock->mFD = nullptr; 1.200 + NS_RELEASE(sock->mHandler); 1.201 + 1.202 + if (listHead == mActiveList) 1.203 + RemoveFromPollList(sock); 1.204 + else 1.205 + RemoveFromIdleList(sock); 1.206 + 1.207 + // NOTE: sock is now an invalid pointer 1.208 + 1.209 + // 1.210 + // notify the first element on the pending socket queue... 1.211 + // 1.212 + nsCOMPtr<nsIRunnable> event; 1.213 + if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) { 1.214 + // move event from pending queue to dispatch queue 1.215 + return Dispatch(event, NS_DISPATCH_NORMAL); 1.216 + } 1.217 + return NS_OK; 1.218 +} 1.219 + 1.220 +nsresult 1.221 +nsSocketTransportService::AddToPollList(SocketContext *sock) 1.222 +{ 1.223 + NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mActiveList)) < mActiveListSize), 1.224 + "AddToPollList Socket Already Active"); 1.225 + 1.226 + SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler)); 1.227 + if (mActiveCount == mActiveListSize) { 1.228 + SOCKET_LOG((" Active List size of %d met\n", mActiveCount)); 1.229 + if (!GrowActiveList()) { 1.230 + NS_ERROR("too many active sockets"); 1.231 + return NS_ERROR_OUT_OF_MEMORY; 1.232 + } 1.233 + } 1.234 + 1.235 + uint32_t newSocketIndex = mActiveCount; 1.236 + if (ChaosMode::isActive()) { 1.237 + newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1); 1.238 + PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex, 1.239 + mActiveCount - newSocketIndex); 1.240 + PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1, 1.241 + mActiveCount - newSocketIndex); 1.242 + } 1.243 + mActiveList[newSocketIndex] = *sock; 1.244 + mActiveCount++; 1.245 + 1.246 + mPollList[newSocketIndex + 1].fd = sock->mFD; 1.247 + mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags; 1.248 + mPollList[newSocketIndex + 1].out_flags = 0; 1.249 + 1.250 + SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); 1.251 + return NS_OK; 1.252 +} 1.253 + 1.254 +void 1.255 +nsSocketTransportService::RemoveFromPollList(SocketContext *sock) 1.256 +{ 1.257 + SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler)); 1.258 + 1.259 + uint32_t index = sock - mActiveList; 1.260 + NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index"); 1.261 + 1.262 + SOCKET_LOG((" index=%u mActiveCount=%u\n", index, mActiveCount)); 1.263 + 1.264 + if (index != mActiveCount-1) { 1.265 + mActiveList[index] = mActiveList[mActiveCount-1]; 1.266 + mPollList[index+1] = mPollList[mActiveCount]; 1.267 + } 1.268 + mActiveCount--; 1.269 + 1.270 + SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); 1.271 +} 1.272 + 1.273 +nsresult 1.274 +nsSocketTransportService::AddToIdleList(SocketContext *sock) 1.275 +{ 1.276 + NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mIdleList)) < mIdleListSize), 1.277 + "AddToIdlelList Socket Already Idle"); 1.278 + 1.279 + SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler)); 1.280 + if (mIdleCount == mIdleListSize) { 1.281 + SOCKET_LOG((" Idle List size of %d met\n", mIdleCount)); 1.282 + if (!GrowIdleList()) { 1.283 + NS_ERROR("too many idle sockets"); 1.284 + return NS_ERROR_OUT_OF_MEMORY; 1.285 + } 1.286 + } 1.287 + 1.288 + mIdleList[mIdleCount] = *sock; 1.289 + mIdleCount++; 1.290 + 1.291 + SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); 1.292 + return NS_OK; 1.293 +} 1.294 + 1.295 +void 1.296 +nsSocketTransportService::RemoveFromIdleList(SocketContext *sock) 1.297 +{ 1.298 + SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler)); 1.299 + 1.300 + uint32_t index = sock - mIdleList; 1.301 + NS_ASSERTION(index < mIdleListSize, "invalid index in idle list"); 1.302 + 1.303 + if (index != mIdleCount-1) 1.304 + mIdleList[index] = mIdleList[mIdleCount-1]; 1.305 + mIdleCount--; 1.306 + 1.307 + SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount)); 1.308 +} 1.309 + 1.310 +void 1.311 +nsSocketTransportService::MoveToIdleList(SocketContext *sock) 1.312 +{ 1.313 + nsresult rv = AddToIdleList(sock); 1.314 + if (NS_FAILED(rv)) 1.315 + DetachSocket(mActiveList, sock); 1.316 + else 1.317 + RemoveFromPollList(sock); 1.318 +} 1.319 + 1.320 +void 1.321 +nsSocketTransportService::MoveToPollList(SocketContext *sock) 1.322 +{ 1.323 + nsresult rv = AddToPollList(sock); 1.324 + if (NS_FAILED(rv)) 1.325 + DetachSocket(mIdleList, sock); 1.326 + else 1.327 + RemoveFromIdleList(sock); 1.328 +} 1.329 + 1.330 +bool 1.331 +nsSocketTransportService::GrowActiveList() 1.332 +{ 1.333 + int32_t toAdd = gMaxCount - mActiveListSize; 1.334 + if (toAdd > 100) 1.335 + toAdd = 100; 1.336 + if (toAdd < 1) 1.337 + return false; 1.338 + 1.339 + mActiveListSize += toAdd; 1.340 + mActiveList = (SocketContext *) 1.341 + moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize); 1.342 + mPollList = (PRPollDesc *) 1.343 + moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1)); 1.344 + return true; 1.345 +} 1.346 + 1.347 +bool 1.348 +nsSocketTransportService::GrowIdleList() 1.349 +{ 1.350 + int32_t toAdd = gMaxCount - mIdleListSize; 1.351 + if (toAdd > 100) 1.352 + toAdd = 100; 1.353 + if (toAdd < 1) 1.354 + return false; 1.355 + 1.356 + mIdleListSize += toAdd; 1.357 + mIdleList = (SocketContext *) 1.358 + moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize); 1.359 + return true; 1.360 +} 1.361 + 1.362 +PRIntervalTime 1.363 +nsSocketTransportService::PollTimeout() 1.364 +{ 1.365 + if (mActiveCount == 0) 1.366 + return NS_SOCKET_POLL_TIMEOUT; 1.367 + 1.368 + // compute minimum time before any socket timeout expires. 1.369 + uint32_t minR = UINT16_MAX; 1.370 + for (uint32_t i=0; i<mActiveCount; ++i) { 1.371 + const SocketContext &s = mActiveList[i]; 1.372 + // mPollTimeout could be less than mElapsedTime if setTimeout 1.373 + // was called with a value smaller than mElapsedTime. 1.374 + uint32_t r = (s.mElapsedTime < s.mHandler->mPollTimeout) 1.375 + ? s.mHandler->mPollTimeout - s.mElapsedTime 1.376 + : 0; 1.377 + if (r < minR) 1.378 + minR = r; 1.379 + } 1.380 + // nsASocketHandler defines UINT16_MAX as do not timeout 1.381 + if (minR == UINT16_MAX) { 1.382 + SOCKET_LOG(("poll timeout: none\n")); 1.383 + return NS_SOCKET_POLL_TIMEOUT; 1.384 + } 1.385 + SOCKET_LOG(("poll timeout: %lu\n", minR)); 1.386 + return PR_SecondsToInterval(minR); 1.387 +} 1.388 + 1.389 +int32_t 1.390 +nsSocketTransportService::Poll(bool wait, uint32_t *interval) 1.391 +{ 1.392 + PRPollDesc *pollList; 1.393 + uint32_t pollCount; 1.394 + PRIntervalTime pollTimeout; 1.395 + 1.396 + if (mPollList[0].fd) { 1.397 + mPollList[0].out_flags = 0; 1.398 + pollList = mPollList; 1.399 + pollCount = mActiveCount + 1; 1.400 + pollTimeout = PollTimeout(); 1.401 + } 1.402 + else { 1.403 + // no pollable event, so busy wait... 1.404 + pollCount = mActiveCount; 1.405 + if (pollCount) 1.406 + pollList = &mPollList[1]; 1.407 + else 1.408 + pollList = nullptr; 1.409 + pollTimeout = PR_MillisecondsToInterval(25); 1.410 + } 1.411 + 1.412 + if (!wait) 1.413 + pollTimeout = PR_INTERVAL_NO_WAIT; 1.414 + 1.415 + PRIntervalTime ts = PR_IntervalNow(); 1.416 + 1.417 + SOCKET_LOG((" timeout = %i milliseconds\n", 1.418 + PR_IntervalToMilliseconds(pollTimeout))); 1.419 + int32_t rv = PR_Poll(pollList, pollCount, pollTimeout); 1.420 + 1.421 + PRIntervalTime passedInterval = PR_IntervalNow() - ts; 1.422 + 1.423 + SOCKET_LOG((" ...returned after %i milliseconds\n", 1.424 + PR_IntervalToMilliseconds(passedInterval))); 1.425 + 1.426 + *interval = PR_IntervalToSeconds(passedInterval); 1.427 + return rv; 1.428 +} 1.429 + 1.430 +//----------------------------------------------------------------------------- 1.431 +// xpcom api 1.432 + 1.433 +NS_IMPL_ISUPPORTS(nsSocketTransportService, 1.434 + nsISocketTransportService, 1.435 + nsIEventTarget, 1.436 + nsIThreadObserver, 1.437 + nsIRunnable, 1.438 + nsPISocketTransportService, 1.439 + nsIObserver) 1.440 + 1.441 +// called from main thread only 1.442 +NS_IMETHODIMP 1.443 +nsSocketTransportService::Init() 1.444 +{ 1.445 + if (!NS_IsMainThread()) { 1.446 + NS_ERROR("wrong thread"); 1.447 + return NS_ERROR_UNEXPECTED; 1.448 + } 1.449 + 1.450 + if (mInitialized) 1.451 + return NS_OK; 1.452 + 1.453 + if (mShuttingDown) 1.454 + return NS_ERROR_UNEXPECTED; 1.455 + 1.456 + if (!mThreadEvent) { 1.457 + mThreadEvent = PR_NewPollableEvent(); 1.458 + // 1.459 + // NOTE: per bug 190000, this failure could be caused by Zone-Alarm 1.460 + // or similar software. 1.461 + // 1.462 + // NOTE: per bug 191739, this failure could also be caused by lack 1.463 + // of a loopback device on Windows and OS/2 platforms (NSPR creates 1.464 + // a loopback socket pair on these platforms to implement a pollable 1.465 + // event object). if we can't create a pollable event, then we'll 1.466 + // have to "busy wait" to implement the socket event queue :-( 1.467 + // 1.468 + if (!mThreadEvent) { 1.469 + NS_WARNING("running socket transport thread without a pollable event"); 1.470 + SOCKET_LOG(("running socket transport thread without a pollable event")); 1.471 + } 1.472 + } 1.473 + 1.474 + nsCOMPtr<nsIThread> thread; 1.475 + nsresult rv = NS_NewThread(getter_AddRefs(thread), this); 1.476 + if (NS_FAILED(rv)) return rv; 1.477 + 1.478 + { 1.479 + MutexAutoLock lock(mLock); 1.480 + // Install our mThread, protecting against concurrent readers 1.481 + thread.swap(mThread); 1.482 + } 1.483 + 1.484 + nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.485 + if (tmpPrefService) { 1.486 + tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false); 1.487 + tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false); 1.488 + tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false); 1.489 + tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false); 1.490 + tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false); 1.491 + } 1.492 + UpdatePrefs(); 1.493 + 1.494 + nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 1.495 + if (obsSvc) { 1.496 + obsSvc->AddObserver(this, "profile-initial-state", false); 1.497 + obsSvc->AddObserver(this, "last-pb-context-exited", false); 1.498 + } 1.499 + 1.500 + mInitialized = true; 1.501 + return NS_OK; 1.502 +} 1.503 + 1.504 +// called from main thread only 1.505 +NS_IMETHODIMP 1.506 +nsSocketTransportService::Shutdown() 1.507 +{ 1.508 + SOCKET_LOG(("nsSocketTransportService::Shutdown\n")); 1.509 + 1.510 + NS_ENSURE_STATE(NS_IsMainThread()); 1.511 + 1.512 + if (!mInitialized) 1.513 + return NS_OK; 1.514 + 1.515 + if (mShuttingDown) 1.516 + return NS_ERROR_UNEXPECTED; 1.517 + 1.518 + { 1.519 + MutexAutoLock lock(mLock); 1.520 + 1.521 + // signal the socket thread to shutdown 1.522 + mShuttingDown = true; 1.523 + 1.524 + if (mThreadEvent) 1.525 + PR_SetPollableEvent(mThreadEvent); 1.526 + // else wait for Poll timeout 1.527 + } 1.528 + 1.529 + // join with thread 1.530 + mThread->Shutdown(); 1.531 + { 1.532 + MutexAutoLock lock(mLock); 1.533 + // Drop our reference to mThread and make sure that any concurrent 1.534 + // readers are excluded 1.535 + mThread = nullptr; 1.536 + } 1.537 + 1.538 + nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.539 + if (tmpPrefService) 1.540 + tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this); 1.541 + 1.542 + nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 1.543 + if (obsSvc) { 1.544 + obsSvc->RemoveObserver(this, "profile-initial-state"); 1.545 + obsSvc->RemoveObserver(this, "last-pb-context-exited"); 1.546 + } 1.547 + 1.548 + mozilla::net::NetworkActivityMonitor::Shutdown(); 1.549 + 1.550 + mInitialized = false; 1.551 + mShuttingDown = false; 1.552 + 1.553 + return NS_OK; 1.554 +} 1.555 + 1.556 +NS_IMETHODIMP 1.557 +nsSocketTransportService::GetOffline(bool *offline) 1.558 +{ 1.559 + *offline = mOffline; 1.560 + return NS_OK; 1.561 +} 1.562 + 1.563 +NS_IMETHODIMP 1.564 +nsSocketTransportService::SetOffline(bool offline) 1.565 +{ 1.566 + MutexAutoLock lock(mLock); 1.567 + if (!mOffline && offline) { 1.568 + // signal the socket thread to go offline, so it will detach sockets 1.569 + mGoingOffline = true; 1.570 + mOffline = true; 1.571 + } 1.572 + else if (mOffline && !offline) { 1.573 + mOffline = false; 1.574 + } 1.575 + if (mThreadEvent) 1.576 + PR_SetPollableEvent(mThreadEvent); 1.577 + 1.578 + return NS_OK; 1.579 +} 1.580 + 1.581 +NS_IMETHODIMP 1.582 +nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS) 1.583 +{ 1.584 + MOZ_ASSERT(aKeepaliveIdleTimeS); 1.585 + if (NS_WARN_IF(!aKeepaliveIdleTimeS)) { 1.586 + return NS_ERROR_NULL_POINTER; 1.587 + } 1.588 + *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS; 1.589 + return NS_OK; 1.590 +} 1.591 + 1.592 +NS_IMETHODIMP 1.593 +nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS) 1.594 +{ 1.595 + MOZ_ASSERT(aKeepaliveRetryIntervalS); 1.596 + if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) { 1.597 + return NS_ERROR_NULL_POINTER; 1.598 + } 1.599 + *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS; 1.600 + return NS_OK; 1.601 +} 1.602 + 1.603 +NS_IMETHODIMP 1.604 +nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount) 1.605 +{ 1.606 + MOZ_ASSERT(aKeepaliveProbeCount); 1.607 + if (NS_WARN_IF(!aKeepaliveProbeCount)) { 1.608 + return NS_ERROR_NULL_POINTER; 1.609 + } 1.610 + *aKeepaliveProbeCount = mKeepaliveProbeCount; 1.611 + return NS_OK; 1.612 +} 1.613 + 1.614 +NS_IMETHODIMP 1.615 +nsSocketTransportService::CreateTransport(const char **types, 1.616 + uint32_t typeCount, 1.617 + const nsACString &host, 1.618 + int32_t port, 1.619 + nsIProxyInfo *proxyInfo, 1.620 + nsISocketTransport **result) 1.621 +{ 1.622 + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); 1.623 + NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE); 1.624 + 1.625 + nsRefPtr<nsSocketTransport> trans = new nsSocketTransport(); 1.626 + nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo); 1.627 + if (NS_FAILED(rv)) { 1.628 + return rv; 1.629 + } 1.630 + 1.631 + trans.forget(result); 1.632 + return NS_OK; 1.633 +} 1.634 + 1.635 +NS_IMETHODIMP 1.636 +nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath, 1.637 + nsISocketTransport **result) 1.638 +{ 1.639 + nsresult rv; 1.640 + 1.641 + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); 1.642 + 1.643 + nsAutoCString path; 1.644 + rv = aPath->GetNativePath(path); 1.645 + if (NS_FAILED(rv)) 1.646 + return rv; 1.647 + 1.648 + nsRefPtr<nsSocketTransport> trans = new nsSocketTransport(); 1.649 + 1.650 + rv = trans->InitWithFilename(path.get()); 1.651 + if (NS_FAILED(rv)) 1.652 + return rv; 1.653 + 1.654 + trans.forget(result); 1.655 + return NS_OK; 1.656 +} 1.657 + 1.658 +NS_IMETHODIMP 1.659 +nsSocketTransportService::GetAutodialEnabled(bool *value) 1.660 +{ 1.661 + *value = mAutodialEnabled; 1.662 + return NS_OK; 1.663 +} 1.664 + 1.665 +NS_IMETHODIMP 1.666 +nsSocketTransportService::SetAutodialEnabled(bool value) 1.667 +{ 1.668 + mAutodialEnabled = value; 1.669 + return NS_OK; 1.670 +} 1.671 + 1.672 +NS_IMETHODIMP 1.673 +nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread) 1.674 +{ 1.675 + MutexAutoLock lock(mLock); 1.676 + if (mThreadEvent) 1.677 + PR_SetPollableEvent(mThreadEvent); 1.678 + return NS_OK; 1.679 +} 1.680 + 1.681 +NS_IMETHODIMP 1.682 +nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread, 1.683 + bool mayWait, uint32_t depth) 1.684 +{ 1.685 + return NS_OK; 1.686 +} 1.687 + 1.688 +NS_IMETHODIMP 1.689 +nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread, 1.690 + uint32_t depth, 1.691 + bool eventWasProcessed) 1.692 +{ 1.693 + return NS_OK; 1.694 +} 1.695 + 1.696 +#ifdef MOZ_NUWA_PROCESS 1.697 +#include "ipc/Nuwa.h" 1.698 +#endif 1.699 + 1.700 +NS_IMETHODIMP 1.701 +nsSocketTransportService::Run() 1.702 +{ 1.703 + PR_SetCurrentThreadName("Socket Thread"); 1.704 + 1.705 +#ifdef MOZ_NUWA_PROCESS 1.706 + if (IsNuwaProcess()) { 1.707 + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, 1.708 + "NuwaMarkCurrentThread is undefined!"); 1.709 + NuwaMarkCurrentThread(nullptr, nullptr); 1.710 + } 1.711 +#endif 1.712 + 1.713 + SOCKET_LOG(("STS thread init\n")); 1.714 + 1.715 + psm::InitializeSSLServerCertVerificationThreads(); 1.716 + 1.717 + gSocketThread = PR_GetCurrentThread(); 1.718 + 1.719 + // add thread event to poll list (mThreadEvent may be nullptr) 1.720 + mPollList[0].fd = mThreadEvent; 1.721 + mPollList[0].in_flags = PR_POLL_READ; 1.722 + mPollList[0].out_flags = 0; 1.723 + 1.724 + nsIThread *thread = NS_GetCurrentThread(); 1.725 + 1.726 + // hook ourselves up to observe event processing for this thread 1.727 + nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread); 1.728 + threadInt->SetObserver(this); 1.729 + 1.730 + // make sure the pseudo random number generator is seeded on this thread 1.731 + srand(static_cast<unsigned>(PR_Now())); 1.732 + 1.733 + for (;;) { 1.734 + bool pendingEvents = false; 1.735 + thread->HasPendingEvents(&pendingEvents); 1.736 + 1.737 + do { 1.738 + // If there are pending events for this thread then 1.739 + // DoPollIteration() should service the network without blocking. 1.740 + DoPollIteration(!pendingEvents); 1.741 + 1.742 + // If nothing was pending before the poll, it might be now 1.743 + if (!pendingEvents) 1.744 + thread->HasPendingEvents(&pendingEvents); 1.745 + 1.746 + if (pendingEvents) { 1.747 + NS_ProcessNextEvent(thread); 1.748 + pendingEvents = false; 1.749 + thread->HasPendingEvents(&pendingEvents); 1.750 + } 1.751 + } while (pendingEvents); 1.752 + 1.753 + bool goingOffline = false; 1.754 + // now that our event queue is empty, check to see if we should exit 1.755 + { 1.756 + MutexAutoLock lock(mLock); 1.757 + if (mShuttingDown) 1.758 + break; 1.759 + if (mGoingOffline) { 1.760 + mGoingOffline = false; 1.761 + goingOffline = true; 1.762 + } 1.763 + } 1.764 + // Avoid potential deadlock 1.765 + if (goingOffline) 1.766 + Reset(true); 1.767 + } 1.768 + 1.769 + SOCKET_LOG(("STS shutting down thread\n")); 1.770 + 1.771 + // detach all sockets, including locals 1.772 + Reset(false); 1.773 + 1.774 + // Final pass over the event queue. This makes sure that events posted by 1.775 + // socket detach handlers get processed. 1.776 + NS_ProcessPendingEvents(thread); 1.777 + 1.778 + gSocketThread = nullptr; 1.779 + 1.780 + psm::StopSSLServerCertVerificationThreads(); 1.781 + 1.782 + SOCKET_LOG(("STS thread exit\n")); 1.783 + return NS_OK; 1.784 +} 1.785 + 1.786 +void 1.787 +nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals, 1.788 + SocketContext *socketList, 1.789 + int32_t index) 1.790 +{ 1.791 + bool isGuarded = false; 1.792 + if (aGuardLocals) { 1.793 + socketList[index].mHandler->IsLocal(&isGuarded); 1.794 + if (!isGuarded) 1.795 + socketList[index].mHandler->KeepWhenOffline(&isGuarded); 1.796 + } 1.797 + if (!isGuarded) 1.798 + DetachSocket(socketList, &socketList[index]); 1.799 +} 1.800 + 1.801 +void 1.802 +nsSocketTransportService::Reset(bool aGuardLocals) 1.803 +{ 1.804 + // detach any sockets 1.805 + int32_t i; 1.806 + for (i = mActiveCount - 1; i >= 0; --i) { 1.807 + DetachSocketWithGuard(aGuardLocals, mActiveList, i); 1.808 + } 1.809 + for (i = mIdleCount - 1; i >= 0; --i) { 1.810 + DetachSocketWithGuard(aGuardLocals, mIdleList, i); 1.811 + } 1.812 +} 1.813 + 1.814 +nsresult 1.815 +nsSocketTransportService::DoPollIteration(bool wait) 1.816 +{ 1.817 + SOCKET_LOG(("STS poll iter [%d]\n", wait)); 1.818 + 1.819 + int32_t i, count; 1.820 + 1.821 + // 1.822 + // poll loop 1.823 + // 1.824 + // walk active list backwards to see if any sockets should actually be 1.825 + // idle, then walk the idle list backwards to see if any idle sockets 1.826 + // should become active. take care to check only idle sockets that 1.827 + // were idle to begin with ;-) 1.828 + // 1.829 + count = mIdleCount; 1.830 + for (i=mActiveCount-1; i>=0; --i) { 1.831 + //--- 1.832 + SOCKET_LOG((" active [%u] { handler=%p condition=%x pollflags=%hu }\n", i, 1.833 + mActiveList[i].mHandler, 1.834 + mActiveList[i].mHandler->mCondition, 1.835 + mActiveList[i].mHandler->mPollFlags)); 1.836 + //--- 1.837 + if (NS_FAILED(mActiveList[i].mHandler->mCondition)) 1.838 + DetachSocket(mActiveList, &mActiveList[i]); 1.839 + else { 1.840 + uint16_t in_flags = mActiveList[i].mHandler->mPollFlags; 1.841 + if (in_flags == 0) 1.842 + MoveToIdleList(&mActiveList[i]); 1.843 + else { 1.844 + // update poll flags 1.845 + mPollList[i+1].in_flags = in_flags; 1.846 + mPollList[i+1].out_flags = 0; 1.847 + } 1.848 + } 1.849 + } 1.850 + for (i=count-1; i>=0; --i) { 1.851 + //--- 1.852 + SOCKET_LOG((" idle [%u] { handler=%p condition=%x pollflags=%hu }\n", i, 1.853 + mIdleList[i].mHandler, 1.854 + mIdleList[i].mHandler->mCondition, 1.855 + mIdleList[i].mHandler->mPollFlags)); 1.856 + //--- 1.857 + if (NS_FAILED(mIdleList[i].mHandler->mCondition)) 1.858 + DetachSocket(mIdleList, &mIdleList[i]); 1.859 + else if (mIdleList[i].mHandler->mPollFlags != 0) 1.860 + MoveToPollList(&mIdleList[i]); 1.861 + } 1.862 + 1.863 + SOCKET_LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount)); 1.864 + 1.865 +#if defined(XP_WIN) 1.866 + // 30 active connections is the historic limit before firefox 7's 256. A few 1.867 + // windows systems have troubles with the higher limit, so actively probe a 1.868 + // limit the first time we exceed 30. 1.869 + if ((mActiveCount > 30) && !mProbedMaxCount) 1.870 + ProbeMaxCount(); 1.871 +#endif 1.872 + 1.873 + // Measures seconds spent while blocked on PR_Poll 1.874 + uint32_t pollInterval; 1.875 + 1.876 + int32_t n = Poll(wait, &pollInterval); 1.877 + if (n < 0) { 1.878 + SOCKET_LOG((" PR_Poll error [%d]\n", PR_GetError())); 1.879 + } 1.880 + else { 1.881 + // 1.882 + // service "active" sockets... 1.883 + // 1.884 + for (i=0; i<int32_t(mActiveCount); ++i) { 1.885 + PRPollDesc &desc = mPollList[i+1]; 1.886 + SocketContext &s = mActiveList[i]; 1.887 + if (n > 0 && desc.out_flags != 0) { 1.888 + s.mElapsedTime = 0; 1.889 + s.mHandler->OnSocketReady(desc.fd, desc.out_flags); 1.890 + } 1.891 + // check for timeout errors unless disabled... 1.892 + else if (s.mHandler->mPollTimeout != UINT16_MAX) { 1.893 + // update elapsed time counter 1.894 + // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value 1.895 + // here -- otherwise, some compilers will treat it as signed, 1.896 + // which makes them fire signed/unsigned-comparison build 1.897 + // warnings for the comparison against 'pollInterval'.) 1.898 + if (MOZ_UNLIKELY(pollInterval > 1.899 + static_cast<uint32_t>(UINT16_MAX) - 1.900 + s.mElapsedTime)) 1.901 + s.mElapsedTime = UINT16_MAX; 1.902 + else 1.903 + s.mElapsedTime += uint16_t(pollInterval); 1.904 + // check for timeout expiration 1.905 + if (s.mElapsedTime >= s.mHandler->mPollTimeout) { 1.906 + s.mElapsedTime = 0; 1.907 + s.mHandler->OnSocketReady(desc.fd, -1); 1.908 + } 1.909 + } 1.910 + } 1.911 + 1.912 + // 1.913 + // check for "dead" sockets and remove them (need to do this in 1.914 + // reverse order obviously). 1.915 + // 1.916 + for (i=mActiveCount-1; i>=0; --i) { 1.917 + if (NS_FAILED(mActiveList[i].mHandler->mCondition)) 1.918 + DetachSocket(mActiveList, &mActiveList[i]); 1.919 + } 1.920 + 1.921 + if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) { 1.922 + // acknowledge pollable event (wait should not block) 1.923 + if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) { 1.924 + // On Windows, the TCP loopback connection in the 1.925 + // pollable event may become broken when a laptop 1.926 + // switches between wired and wireless networks or 1.927 + // wakes up from hibernation. We try to create a 1.928 + // new pollable event. If that fails, we fall back 1.929 + // on "busy wait". 1.930 + { 1.931 + MutexAutoLock lock(mLock); 1.932 + PR_DestroyPollableEvent(mThreadEvent); 1.933 + mThreadEvent = PR_NewPollableEvent(); 1.934 + } 1.935 + if (!mThreadEvent) { 1.936 + NS_WARNING("running socket transport thread without " 1.937 + "a pollable event"); 1.938 + SOCKET_LOG(("running socket transport thread without " 1.939 + "a pollable event")); 1.940 + } 1.941 + mPollList[0].fd = mThreadEvent; 1.942 + // mPollList[0].in_flags was already set to PR_POLL_READ 1.943 + // in Run(). 1.944 + mPollList[0].out_flags = 0; 1.945 + } 1.946 + } 1.947 + } 1.948 + 1.949 + return NS_OK; 1.950 +} 1.951 + 1.952 +nsresult 1.953 +nsSocketTransportService::UpdatePrefs() 1.954 +{ 1.955 + mSendBufferSize = 0; 1.956 + 1.957 + nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.958 + if (tmpPrefService) { 1.959 + int32_t bufferSize; 1.960 + nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize); 1.961 + if (NS_SUCCEEDED(rv) && bufferSize > 0) 1.962 + mSendBufferSize = bufferSize; 1.963 + 1.964 + // Default TCP Keepalive Values. 1.965 + int32_t keepaliveIdleTimeS; 1.966 + rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF, 1.967 + &keepaliveIdleTimeS); 1.968 + if (NS_SUCCEEDED(rv)) 1.969 + mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS, 1.970 + 1, kMaxTCPKeepIdle); 1.971 + 1.972 + int32_t keepaliveRetryIntervalS; 1.973 + rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF, 1.974 + &keepaliveRetryIntervalS); 1.975 + if (NS_SUCCEEDED(rv)) 1.976 + mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS, 1.977 + 1, kMaxTCPKeepIntvl); 1.978 + 1.979 + int32_t keepaliveProbeCount; 1.980 + rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF, 1.981 + &keepaliveProbeCount); 1.982 + if (NS_SUCCEEDED(rv)) 1.983 + mKeepaliveProbeCount = clamped(keepaliveProbeCount, 1.984 + 1, kMaxTCPKeepCount); 1.985 + bool keepaliveEnabled = false; 1.986 + rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF, 1.987 + &keepaliveEnabled); 1.988 + if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) { 1.989 + mKeepaliveEnabledPref = keepaliveEnabled; 1.990 + OnKeepaliveEnabledPrefChange(); 1.991 + } 1.992 + } 1.993 + 1.994 + return NS_OK; 1.995 +} 1.996 + 1.997 +void 1.998 +nsSocketTransportService::OnKeepaliveEnabledPrefChange() 1.999 +{ 1.1000 + // Dispatch to socket thread if we're not executing there. 1.1001 + if (PR_GetCurrentThread() != gSocketThread) { 1.1002 + gSocketTransportService->Dispatch( 1.1003 + NS_NewRunnableMethod( 1.1004 + this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange), 1.1005 + NS_DISPATCH_NORMAL); 1.1006 + return; 1.1007 + } 1.1008 + 1.1009 + SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s", 1.1010 + mKeepaliveEnabledPref ? "enabled" : "disabled")); 1.1011 + 1.1012 + // Notify each socket that keepalive has been en/disabled globally. 1.1013 + for (int32_t i = mActiveCount - 1; i >= 0; --i) { 1.1014 + NotifyKeepaliveEnabledPrefChange(&mActiveList[i]); 1.1015 + } 1.1016 + for (int32_t i = mIdleCount - 1; i >= 0; --i) { 1.1017 + NotifyKeepaliveEnabledPrefChange(&mIdleList[i]); 1.1018 + } 1.1019 +} 1.1020 + 1.1021 +void 1.1022 +nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock) 1.1023 +{ 1.1024 + MOZ_ASSERT(sock, "SocketContext cannot be null!"); 1.1025 + MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!"); 1.1026 + 1.1027 + if (!sock || !sock->mHandler) { 1.1028 + return; 1.1029 + } 1.1030 + 1.1031 + sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref); 1.1032 +} 1.1033 + 1.1034 +NS_IMETHODIMP 1.1035 +nsSocketTransportService::Observe(nsISupports *subject, 1.1036 + const char *topic, 1.1037 + const char16_t *data) 1.1038 +{ 1.1039 + if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { 1.1040 + UpdatePrefs(); 1.1041 + return NS_OK; 1.1042 + } 1.1043 + 1.1044 + if (!strcmp(topic, "profile-initial-state")) { 1.1045 + int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0); 1.1046 + if (blipInterval <= 0) { 1.1047 + return NS_OK; 1.1048 + } 1.1049 + 1.1050 + return net::NetworkActivityMonitor::Init(blipInterval); 1.1051 + } 1.1052 + 1.1053 + if (!strcmp(topic, "last-pb-context-exited")) { 1.1054 + nsCOMPtr<nsIRunnable> ev = 1.1055 + NS_NewRunnableMethod(this, 1.1056 + &nsSocketTransportService::ClosePrivateConnections); 1.1057 + nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL); 1.1058 + NS_ENSURE_SUCCESS(rv, rv); 1.1059 + } 1.1060 + 1.1061 + return NS_OK; 1.1062 +} 1.1063 + 1.1064 +void 1.1065 +nsSocketTransportService::ClosePrivateConnections() 1.1066 +{ 1.1067 + // Must be called on the socket thread. 1.1068 +#ifdef DEBUG 1.1069 + bool onSTSThread; 1.1070 + IsOnCurrentThread(&onSTSThread); 1.1071 + MOZ_ASSERT(onSTSThread); 1.1072 +#endif 1.1073 + 1.1074 + for (int32_t i = mActiveCount - 1; i >= 0; --i) { 1.1075 + if (mActiveList[i].mHandler->mIsPrivate) { 1.1076 + DetachSocket(mActiveList, &mActiveList[i]); 1.1077 + } 1.1078 + } 1.1079 + for (int32_t i = mIdleCount - 1; i >= 0; --i) { 1.1080 + if (mIdleList[i].mHandler->mIsPrivate) { 1.1081 + DetachSocket(mIdleList, &mIdleList[i]); 1.1082 + } 1.1083 + } 1.1084 + 1.1085 + mozilla::ClearPrivateSSLState(); 1.1086 +} 1.1087 + 1.1088 +NS_IMETHODIMP 1.1089 +nsSocketTransportService::GetSendBufferSize(int32_t *value) 1.1090 +{ 1.1091 + *value = mSendBufferSize; 1.1092 + return NS_OK; 1.1093 +} 1.1094 + 1.1095 + 1.1096 +/// ugly OS specific includes are placed at the bottom of the src for clarity 1.1097 + 1.1098 +#if defined(XP_WIN) 1.1099 +#include <windows.h> 1.1100 +#elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX) 1.1101 +#include <sys/resource.h> 1.1102 +#endif 1.1103 + 1.1104 +// Right now the only need to do this is on windows. 1.1105 +#if defined(XP_WIN) 1.1106 +void 1.1107 +nsSocketTransportService::ProbeMaxCount() 1.1108 +{ 1.1109 + NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); 1.1110 + 1.1111 + if (mProbedMaxCount) 1.1112 + return; 1.1113 + mProbedMaxCount = true; 1.1114 + 1.1115 + // Allocate and test a PR_Poll up to the gMaxCount number of unconnected 1.1116 + // sockets. See bug 692260 - windows should be able to handle 1000 sockets 1.1117 + // in select() without a problem, but LSPs have been known to balk at lower 1.1118 + // numbers. (64 in the bug). 1.1119 + 1.1120 + // Allocate 1.1121 + struct PRPollDesc pfd[SOCKET_LIMIT_TARGET]; 1.1122 + uint32_t numAllocated = 0; 1.1123 + 1.1124 + for (uint32_t index = 0 ; index < gMaxCount; ++index) { 1.1125 + pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT; 1.1126 + pfd[index].out_flags = 0; 1.1127 + pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET); 1.1128 + if (!pfd[index].fd) { 1.1129 + SOCKET_LOG(("Socket Limit Test index %d failed\n", index)); 1.1130 + if (index < SOCKET_LIMIT_MIN) 1.1131 + gMaxCount = SOCKET_LIMIT_MIN; 1.1132 + else 1.1133 + gMaxCount = index; 1.1134 + break; 1.1135 + } 1.1136 + ++numAllocated; 1.1137 + } 1.1138 + 1.1139 + // Test 1.1140 + PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U); 1.1141 + while (gMaxCount <= numAllocated) { 1.1142 + int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0)); 1.1143 + 1.1144 + SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n", 1.1145 + gMaxCount, rv)); 1.1146 + 1.1147 + if (rv >= 0) 1.1148 + break; 1.1149 + 1.1150 + SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n", 1.1151 + gMaxCount, rv, PR_GetError())); 1.1152 + 1.1153 + gMaxCount -= 32; 1.1154 + if (gMaxCount <= SOCKET_LIMIT_MIN) { 1.1155 + gMaxCount = SOCKET_LIMIT_MIN; 1.1156 + break; 1.1157 + } 1.1158 + } 1.1159 + 1.1160 + // Free 1.1161 + for (uint32_t index = 0 ; index < numAllocated; ++index) 1.1162 + if (pfd[index].fd) 1.1163 + PR_Close(pfd[index].fd); 1.1164 + 1.1165 + SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount)); 1.1166 +} 1.1167 +#endif // windows 1.1168 + 1.1169 +PRStatus 1.1170 +nsSocketTransportService::DiscoverMaxCount() 1.1171 +{ 1.1172 + gMaxCount = SOCKET_LIMIT_MIN; 1.1173 + 1.1174 +#if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX) 1.1175 + // On unix and os x network sockets and file 1.1176 + // descriptors are the same. OS X comes defaulted at 256, 1.1177 + // most linux at 1000. We can reliably use [sg]rlimit to 1.1178 + // query that and raise it. We will try to raise it 250 past 1.1179 + // our target number of SOCKET_LIMIT_TARGET so that some descriptors 1.1180 + // are still available for other things. 1.1181 + 1.1182 + struct rlimit rlimitData; 1.1183 + if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) 1.1184 + return PR_SUCCESS; 1.1185 + if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET + 250) { 1.1186 + gMaxCount = SOCKET_LIMIT_TARGET; 1.1187 + return PR_SUCCESS; 1.1188 + } 1.1189 + 1.1190 + int32_t maxallowed = rlimitData.rlim_max; 1.1191 + if (maxallowed == -1) { /* no limit */ 1.1192 + maxallowed = SOCKET_LIMIT_TARGET + 250; 1.1193 + } else if ((uint32_t)maxallowed < SOCKET_LIMIT_MIN + 250) { 1.1194 + return PR_SUCCESS; 1.1195 + } else if ((uint32_t)maxallowed > SOCKET_LIMIT_TARGET + 250) { 1.1196 + maxallowed = SOCKET_LIMIT_TARGET + 250; 1.1197 + } 1.1198 + 1.1199 + rlimitData.rlim_cur = maxallowed; 1.1200 + setrlimit(RLIMIT_NOFILE, &rlimitData); 1.1201 + if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) 1.1202 + if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250) 1.1203 + gMaxCount = rlimitData.rlim_cur - 250; 1.1204 + 1.1205 +#elif defined(XP_WIN) && !defined(WIN_CE) 1.1206 + // >= XP is confirmed to have at least 1000 1.1207 + gMaxCount = SOCKET_LIMIT_TARGET; 1.1208 +#else 1.1209 + // other platforms are harder to test - so leave at safe legacy value 1.1210 +#endif 1.1211 + 1.1212 + return PR_SUCCESS; 1.1213 +} 1.1214 + 1.1215 + 1.1216 +// Used to return connection info to Dashboard.cpp 1.1217 +void 1.1218 +nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data, 1.1219 + struct SocketContext *context, bool aActive) 1.1220 +{ 1.1221 + if (context->mHandler->mIsPrivate) 1.1222 + return; 1.1223 + PRFileDesc *aFD = context->mFD; 1.1224 + bool tcp = (PR_GetDescType(aFD) == PR_DESC_SOCKET_TCP); 1.1225 + 1.1226 + PRNetAddr peer_addr; 1.1227 + PR_GetPeerName(aFD, &peer_addr); 1.1228 + 1.1229 + char host[64] = {0}; 1.1230 + PR_NetAddrToString(&peer_addr, host, sizeof(host)); 1.1231 + 1.1232 + uint16_t port; 1.1233 + if (peer_addr.raw.family == PR_AF_INET) 1.1234 + port = peer_addr.inet.port; 1.1235 + else 1.1236 + port = peer_addr.ipv6.port; 1.1237 + port = PR_ntohs(port); 1.1238 + uint64_t sent = context->mHandler->ByteCountSent(); 1.1239 + uint64_t received = context->mHandler->ByteCountReceived(); 1.1240 + SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp }; 1.1241 + 1.1242 + data->AppendElement(info); 1.1243 +} 1.1244 + 1.1245 +void 1.1246 +nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data) 1.1247 +{ 1.1248 + NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); 1.1249 + for (uint32_t i = 0; i < mActiveCount; i++) 1.1250 + AnalyzeConnection(data, &mActiveList[i], true); 1.1251 + for (uint32_t i = 0; i < mIdleCount; i++) 1.1252 + AnalyzeConnection(data, &mIdleList[i], false); 1.1253 +}