netwerk/base/src/nsSocketTransportService2.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f11ba7063ee8
1 // vim:set sw=4 sts=4 et cin:
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #ifdef MOZ_LOGGING
7 #define FORCE_PR_LOG
8 #endif
9
10 #include "nsSocketTransportService2.h"
11 #include "nsSocketTransport2.h"
12 #include "nsError.h"
13 #include "prnetdb.h"
14 #include "prerror.h"
15 #include "nsIPrefService.h"
16 #include "nsIPrefBranch.h"
17 #include "nsServiceManagerUtils.h"
18 #include "NetworkActivityMonitor.h"
19 #include "nsIObserverService.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/Likely.h"
23 #include "mozilla/PublicSSL.h"
24 #include "mozilla/ChaosMode.h"
25 #include "mozilla/PodOperations.h"
26 #include "nsThreadUtils.h"
27 #include "nsIFile.h"
28
29 using namespace mozilla;
30 using namespace mozilla::net;
31
32 #if defined(PR_LOGGING)
33 PRLogModuleInfo *gSocketTransportLog = nullptr;
34 #endif
35
36 nsSocketTransportService *gSocketTransportService = nullptr;
37 PRThread *gSocketThread = nullptr;
38
39 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
40 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
41 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
42 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
43 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
44 #define SOCKET_LIMIT_TARGET 550U
45 #define SOCKET_LIMIT_MIN 50U
46 #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
47
48 uint32_t nsSocketTransportService::gMaxCount;
49 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
50
51 //-----------------------------------------------------------------------------
52 // ctor/dtor (called on the main/UI thread by the service manager)
53
54 nsSocketTransportService::nsSocketTransportService()
55 : mThread(nullptr)
56 , mThreadEvent(nullptr)
57 , mAutodialEnabled(false)
58 , mLock("nsSocketTransportService::mLock")
59 , mInitialized(false)
60 , mShuttingDown(false)
61 , mOffline(false)
62 , mGoingOffline(false)
63 , mActiveListSize(SOCKET_LIMIT_MIN)
64 , mIdleListSize(SOCKET_LIMIT_MIN)
65 , mActiveCount(0)
66 , mIdleCount(0)
67 , mSentBytesCount(0)
68 , mReceivedBytesCount(0)
69 , mSendBufferSize(0)
70 , mKeepaliveIdleTimeS(600)
71 , mKeepaliveRetryIntervalS(1)
72 , mKeepaliveProbeCount(kDefaultTCPKeepCount)
73 , mKeepaliveEnabledPref(false)
74 , mProbedMaxCount(false)
75 {
76 #if defined(PR_LOGGING)
77 gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
78 #endif
79
80 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
81
82 PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
83 mActiveList = (SocketContext *)
84 moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
85 mIdleList = (SocketContext *)
86 moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
87 mPollList = (PRPollDesc *)
88 moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
89
90 NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
91 gSocketTransportService = this;
92 }
93
94 nsSocketTransportService::~nsSocketTransportService()
95 {
96 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
97 NS_ASSERTION(!mInitialized, "not shutdown properly");
98
99 if (mThreadEvent)
100 PR_DestroyPollableEvent(mThreadEvent);
101
102 moz_free(mActiveList);
103 moz_free(mIdleList);
104 moz_free(mPollList);
105 gSocketTransportService = nullptr;
106 }
107
108 //-----------------------------------------------------------------------------
109 // event queue (any thread)
110
111 already_AddRefed<nsIThread>
112 nsSocketTransportService::GetThreadSafely()
113 {
114 MutexAutoLock lock(mLock);
115 nsCOMPtr<nsIThread> result = mThread;
116 return result.forget();
117 }
118
119 NS_IMETHODIMP
120 nsSocketTransportService::Dispatch(nsIRunnable *event, uint32_t flags)
121 {
122 SOCKET_LOG(("STS dispatch [%p]\n", event));
123
124 nsCOMPtr<nsIThread> thread = GetThreadSafely();
125 nsresult rv;
126 rv = thread ? thread->Dispatch(event, flags) : NS_ERROR_NOT_INITIALIZED;
127 if (rv == NS_ERROR_UNEXPECTED) {
128 // Thread is no longer accepting events. We must have just shut it
129 // down on the main thread. Pretend we never saw it.
130 rv = NS_ERROR_NOT_INITIALIZED;
131 }
132 return rv;
133 }
134
135 NS_IMETHODIMP
136 nsSocketTransportService::IsOnCurrentThread(bool *result)
137 {
138 nsCOMPtr<nsIThread> thread = GetThreadSafely();
139 NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
140 return thread->IsOnCurrentThread(result);
141 }
142
143 //-----------------------------------------------------------------------------
144 // socket api (socket thread only)
145
146 NS_IMETHODIMP
147 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
148 {
149 SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
150
151 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
152
153 if (CanAttachSocket()) {
154 return Dispatch(event, NS_DISPATCH_NORMAL);
155 }
156
157 mPendingSocketQ.PutEvent(event);
158 return NS_OK;
159 }
160
161 NS_IMETHODIMP
162 nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
163 {
164 SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
165
166 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
167
168 if (!CanAttachSocket()) {
169 return NS_ERROR_NOT_AVAILABLE;
170 }
171
172 SocketContext sock;
173 sock.mFD = fd;
174 sock.mHandler = handler;
175 sock.mElapsedTime = 0;
176
177 nsresult rv = AddToIdleList(&sock);
178 if (NS_SUCCEEDED(rv))
179 NS_ADDREF(handler);
180 return rv;
181 }
182
183 nsresult
184 nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
185 {
186 SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler));
187 NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList),
188 "DetachSocket invalid head");
189
190 // inform the handler that this socket is going away
191 sock->mHandler->OnSocketDetached(sock->mFD);
192 mSentBytesCount += sock->mHandler->ByteCountSent();
193 mReceivedBytesCount += sock->mHandler->ByteCountReceived();
194
195 // cleanup
196 sock->mFD = nullptr;
197 NS_RELEASE(sock->mHandler);
198
199 if (listHead == mActiveList)
200 RemoveFromPollList(sock);
201 else
202 RemoveFromIdleList(sock);
203
204 // NOTE: sock is now an invalid pointer
205
206 //
207 // notify the first element on the pending socket queue...
208 //
209 nsCOMPtr<nsIRunnable> event;
210 if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) {
211 // move event from pending queue to dispatch queue
212 return Dispatch(event, NS_DISPATCH_NORMAL);
213 }
214 return NS_OK;
215 }
216
217 nsresult
218 nsSocketTransportService::AddToPollList(SocketContext *sock)
219 {
220 NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mActiveList)) < mActiveListSize),
221 "AddToPollList Socket Already Active");
222
223 SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler));
224 if (mActiveCount == mActiveListSize) {
225 SOCKET_LOG((" Active List size of %d met\n", mActiveCount));
226 if (!GrowActiveList()) {
227 NS_ERROR("too many active sockets");
228 return NS_ERROR_OUT_OF_MEMORY;
229 }
230 }
231
232 uint32_t newSocketIndex = mActiveCount;
233 if (ChaosMode::isActive()) {
234 newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
235 PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
236 mActiveCount - newSocketIndex);
237 PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
238 mActiveCount - newSocketIndex);
239 }
240 mActiveList[newSocketIndex] = *sock;
241 mActiveCount++;
242
243 mPollList[newSocketIndex + 1].fd = sock->mFD;
244 mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags;
245 mPollList[newSocketIndex + 1].out_flags = 0;
246
247 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
248 return NS_OK;
249 }
250
251 void
252 nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
253 {
254 SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler));
255
256 uint32_t index = sock - mActiveList;
257 NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index");
258
259 SOCKET_LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
260
261 if (index != mActiveCount-1) {
262 mActiveList[index] = mActiveList[mActiveCount-1];
263 mPollList[index+1] = mPollList[mActiveCount];
264 }
265 mActiveCount--;
266
267 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
268 }
269
270 nsresult
271 nsSocketTransportService::AddToIdleList(SocketContext *sock)
272 {
273 NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mIdleList)) < mIdleListSize),
274 "AddToIdlelList Socket Already Idle");
275
276 SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler));
277 if (mIdleCount == mIdleListSize) {
278 SOCKET_LOG((" Idle List size of %d met\n", mIdleCount));
279 if (!GrowIdleList()) {
280 NS_ERROR("too many idle sockets");
281 return NS_ERROR_OUT_OF_MEMORY;
282 }
283 }
284
285 mIdleList[mIdleCount] = *sock;
286 mIdleCount++;
287
288 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
289 return NS_OK;
290 }
291
292 void
293 nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
294 {
295 SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler));
296
297 uint32_t index = sock - mIdleList;
298 NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
299
300 if (index != mIdleCount-1)
301 mIdleList[index] = mIdleList[mIdleCount-1];
302 mIdleCount--;
303
304 SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
305 }
306
307 void
308 nsSocketTransportService::MoveToIdleList(SocketContext *sock)
309 {
310 nsresult rv = AddToIdleList(sock);
311 if (NS_FAILED(rv))
312 DetachSocket(mActiveList, sock);
313 else
314 RemoveFromPollList(sock);
315 }
316
317 void
318 nsSocketTransportService::MoveToPollList(SocketContext *sock)
319 {
320 nsresult rv = AddToPollList(sock);
321 if (NS_FAILED(rv))
322 DetachSocket(mIdleList, sock);
323 else
324 RemoveFromIdleList(sock);
325 }
326
327 bool
328 nsSocketTransportService::GrowActiveList()
329 {
330 int32_t toAdd = gMaxCount - mActiveListSize;
331 if (toAdd > 100)
332 toAdd = 100;
333 if (toAdd < 1)
334 return false;
335
336 mActiveListSize += toAdd;
337 mActiveList = (SocketContext *)
338 moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
339 mPollList = (PRPollDesc *)
340 moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
341 return true;
342 }
343
344 bool
345 nsSocketTransportService::GrowIdleList()
346 {
347 int32_t toAdd = gMaxCount - mIdleListSize;
348 if (toAdd > 100)
349 toAdd = 100;
350 if (toAdd < 1)
351 return false;
352
353 mIdleListSize += toAdd;
354 mIdleList = (SocketContext *)
355 moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
356 return true;
357 }
358
359 PRIntervalTime
360 nsSocketTransportService::PollTimeout()
361 {
362 if (mActiveCount == 0)
363 return NS_SOCKET_POLL_TIMEOUT;
364
365 // compute minimum time before any socket timeout expires.
366 uint32_t minR = UINT16_MAX;
367 for (uint32_t i=0; i<mActiveCount; ++i) {
368 const SocketContext &s = mActiveList[i];
369 // mPollTimeout could be less than mElapsedTime if setTimeout
370 // was called with a value smaller than mElapsedTime.
371 uint32_t r = (s.mElapsedTime < s.mHandler->mPollTimeout)
372 ? s.mHandler->mPollTimeout - s.mElapsedTime
373 : 0;
374 if (r < minR)
375 minR = r;
376 }
377 // nsASocketHandler defines UINT16_MAX as do not timeout
378 if (minR == UINT16_MAX) {
379 SOCKET_LOG(("poll timeout: none\n"));
380 return NS_SOCKET_POLL_TIMEOUT;
381 }
382 SOCKET_LOG(("poll timeout: %lu\n", minR));
383 return PR_SecondsToInterval(minR);
384 }
385
386 int32_t
387 nsSocketTransportService::Poll(bool wait, uint32_t *interval)
388 {
389 PRPollDesc *pollList;
390 uint32_t pollCount;
391 PRIntervalTime pollTimeout;
392
393 if (mPollList[0].fd) {
394 mPollList[0].out_flags = 0;
395 pollList = mPollList;
396 pollCount = mActiveCount + 1;
397 pollTimeout = PollTimeout();
398 }
399 else {
400 // no pollable event, so busy wait...
401 pollCount = mActiveCount;
402 if (pollCount)
403 pollList = &mPollList[1];
404 else
405 pollList = nullptr;
406 pollTimeout = PR_MillisecondsToInterval(25);
407 }
408
409 if (!wait)
410 pollTimeout = PR_INTERVAL_NO_WAIT;
411
412 PRIntervalTime ts = PR_IntervalNow();
413
414 SOCKET_LOG((" timeout = %i milliseconds\n",
415 PR_IntervalToMilliseconds(pollTimeout)));
416 int32_t rv = PR_Poll(pollList, pollCount, pollTimeout);
417
418 PRIntervalTime passedInterval = PR_IntervalNow() - ts;
419
420 SOCKET_LOG((" ...returned after %i milliseconds\n",
421 PR_IntervalToMilliseconds(passedInterval)));
422
423 *interval = PR_IntervalToSeconds(passedInterval);
424 return rv;
425 }
426
427 //-----------------------------------------------------------------------------
428 // xpcom api
429
430 NS_IMPL_ISUPPORTS(nsSocketTransportService,
431 nsISocketTransportService,
432 nsIEventTarget,
433 nsIThreadObserver,
434 nsIRunnable,
435 nsPISocketTransportService,
436 nsIObserver)
437
438 // called from main thread only
439 NS_IMETHODIMP
440 nsSocketTransportService::Init()
441 {
442 if (!NS_IsMainThread()) {
443 NS_ERROR("wrong thread");
444 return NS_ERROR_UNEXPECTED;
445 }
446
447 if (mInitialized)
448 return NS_OK;
449
450 if (mShuttingDown)
451 return NS_ERROR_UNEXPECTED;
452
453 if (!mThreadEvent) {
454 mThreadEvent = PR_NewPollableEvent();
455 //
456 // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
457 // or similar software.
458 //
459 // NOTE: per bug 191739, this failure could also be caused by lack
460 // of a loopback device on Windows and OS/2 platforms (NSPR creates
461 // a loopback socket pair on these platforms to implement a pollable
462 // event object). if we can't create a pollable event, then we'll
463 // have to "busy wait" to implement the socket event queue :-(
464 //
465 if (!mThreadEvent) {
466 NS_WARNING("running socket transport thread without a pollable event");
467 SOCKET_LOG(("running socket transport thread without a pollable event"));
468 }
469 }
470
471 nsCOMPtr<nsIThread> thread;
472 nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
473 if (NS_FAILED(rv)) return rv;
474
475 {
476 MutexAutoLock lock(mLock);
477 // Install our mThread, protecting against concurrent readers
478 thread.swap(mThread);
479 }
480
481 nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
482 if (tmpPrefService) {
483 tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
484 tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
485 tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
486 tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
487 tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
488 }
489 UpdatePrefs();
490
491 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
492 if (obsSvc) {
493 obsSvc->AddObserver(this, "profile-initial-state", false);
494 obsSvc->AddObserver(this, "last-pb-context-exited", false);
495 }
496
497 mInitialized = true;
498 return NS_OK;
499 }
500
501 // called from main thread only
502 NS_IMETHODIMP
503 nsSocketTransportService::Shutdown()
504 {
505 SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
506
507 NS_ENSURE_STATE(NS_IsMainThread());
508
509 if (!mInitialized)
510 return NS_OK;
511
512 if (mShuttingDown)
513 return NS_ERROR_UNEXPECTED;
514
515 {
516 MutexAutoLock lock(mLock);
517
518 // signal the socket thread to shutdown
519 mShuttingDown = true;
520
521 if (mThreadEvent)
522 PR_SetPollableEvent(mThreadEvent);
523 // else wait for Poll timeout
524 }
525
526 // join with thread
527 mThread->Shutdown();
528 {
529 MutexAutoLock lock(mLock);
530 // Drop our reference to mThread and make sure that any concurrent
531 // readers are excluded
532 mThread = nullptr;
533 }
534
535 nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
536 if (tmpPrefService)
537 tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
538
539 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
540 if (obsSvc) {
541 obsSvc->RemoveObserver(this, "profile-initial-state");
542 obsSvc->RemoveObserver(this, "last-pb-context-exited");
543 }
544
545 mozilla::net::NetworkActivityMonitor::Shutdown();
546
547 mInitialized = false;
548 mShuttingDown = false;
549
550 return NS_OK;
551 }
552
553 NS_IMETHODIMP
554 nsSocketTransportService::GetOffline(bool *offline)
555 {
556 *offline = mOffline;
557 return NS_OK;
558 }
559
560 NS_IMETHODIMP
561 nsSocketTransportService::SetOffline(bool offline)
562 {
563 MutexAutoLock lock(mLock);
564 if (!mOffline && offline) {
565 // signal the socket thread to go offline, so it will detach sockets
566 mGoingOffline = true;
567 mOffline = true;
568 }
569 else if (mOffline && !offline) {
570 mOffline = false;
571 }
572 if (mThreadEvent)
573 PR_SetPollableEvent(mThreadEvent);
574
575 return NS_OK;
576 }
577
578 NS_IMETHODIMP
579 nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
580 {
581 MOZ_ASSERT(aKeepaliveIdleTimeS);
582 if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
583 return NS_ERROR_NULL_POINTER;
584 }
585 *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
586 return NS_OK;
587 }
588
589 NS_IMETHODIMP
590 nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS)
591 {
592 MOZ_ASSERT(aKeepaliveRetryIntervalS);
593 if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
594 return NS_ERROR_NULL_POINTER;
595 }
596 *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
597 return NS_OK;
598 }
599
600 NS_IMETHODIMP
601 nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount)
602 {
603 MOZ_ASSERT(aKeepaliveProbeCount);
604 if (NS_WARN_IF(!aKeepaliveProbeCount)) {
605 return NS_ERROR_NULL_POINTER;
606 }
607 *aKeepaliveProbeCount = mKeepaliveProbeCount;
608 return NS_OK;
609 }
610
611 NS_IMETHODIMP
612 nsSocketTransportService::CreateTransport(const char **types,
613 uint32_t typeCount,
614 const nsACString &host,
615 int32_t port,
616 nsIProxyInfo *proxyInfo,
617 nsISocketTransport **result)
618 {
619 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
620 NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
621
622 nsRefPtr<nsSocketTransport> trans = new nsSocketTransport();
623 nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
624 if (NS_FAILED(rv)) {
625 return rv;
626 }
627
628 trans.forget(result);
629 return NS_OK;
630 }
631
632 NS_IMETHODIMP
633 nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath,
634 nsISocketTransport **result)
635 {
636 nsresult rv;
637
638 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
639
640 nsAutoCString path;
641 rv = aPath->GetNativePath(path);
642 if (NS_FAILED(rv))
643 return rv;
644
645 nsRefPtr<nsSocketTransport> trans = new nsSocketTransport();
646
647 rv = trans->InitWithFilename(path.get());
648 if (NS_FAILED(rv))
649 return rv;
650
651 trans.forget(result);
652 return NS_OK;
653 }
654
655 NS_IMETHODIMP
656 nsSocketTransportService::GetAutodialEnabled(bool *value)
657 {
658 *value = mAutodialEnabled;
659 return NS_OK;
660 }
661
662 NS_IMETHODIMP
663 nsSocketTransportService::SetAutodialEnabled(bool value)
664 {
665 mAutodialEnabled = value;
666 return NS_OK;
667 }
668
669 NS_IMETHODIMP
670 nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
671 {
672 MutexAutoLock lock(mLock);
673 if (mThreadEvent)
674 PR_SetPollableEvent(mThreadEvent);
675 return NS_OK;
676 }
677
678 NS_IMETHODIMP
679 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
680 bool mayWait, uint32_t depth)
681 {
682 return NS_OK;
683 }
684
685 NS_IMETHODIMP
686 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
687 uint32_t depth,
688 bool eventWasProcessed)
689 {
690 return NS_OK;
691 }
692
693 #ifdef MOZ_NUWA_PROCESS
694 #include "ipc/Nuwa.h"
695 #endif
696
697 NS_IMETHODIMP
698 nsSocketTransportService::Run()
699 {
700 PR_SetCurrentThreadName("Socket Thread");
701
702 #ifdef MOZ_NUWA_PROCESS
703 if (IsNuwaProcess()) {
704 NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
705 "NuwaMarkCurrentThread is undefined!");
706 NuwaMarkCurrentThread(nullptr, nullptr);
707 }
708 #endif
709
710 SOCKET_LOG(("STS thread init\n"));
711
712 psm::InitializeSSLServerCertVerificationThreads();
713
714 gSocketThread = PR_GetCurrentThread();
715
716 // add thread event to poll list (mThreadEvent may be nullptr)
717 mPollList[0].fd = mThreadEvent;
718 mPollList[0].in_flags = PR_POLL_READ;
719 mPollList[0].out_flags = 0;
720
721 nsIThread *thread = NS_GetCurrentThread();
722
723 // hook ourselves up to observe event processing for this thread
724 nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
725 threadInt->SetObserver(this);
726
727 // make sure the pseudo random number generator is seeded on this thread
728 srand(static_cast<unsigned>(PR_Now()));
729
730 for (;;) {
731 bool pendingEvents = false;
732 thread->HasPendingEvents(&pendingEvents);
733
734 do {
735 // If there are pending events for this thread then
736 // DoPollIteration() should service the network without blocking.
737 DoPollIteration(!pendingEvents);
738
739 // If nothing was pending before the poll, it might be now
740 if (!pendingEvents)
741 thread->HasPendingEvents(&pendingEvents);
742
743 if (pendingEvents) {
744 NS_ProcessNextEvent(thread);
745 pendingEvents = false;
746 thread->HasPendingEvents(&pendingEvents);
747 }
748 } while (pendingEvents);
749
750 bool goingOffline = false;
751 // now that our event queue is empty, check to see if we should exit
752 {
753 MutexAutoLock lock(mLock);
754 if (mShuttingDown)
755 break;
756 if (mGoingOffline) {
757 mGoingOffline = false;
758 goingOffline = true;
759 }
760 }
761 // Avoid potential deadlock
762 if (goingOffline)
763 Reset(true);
764 }
765
766 SOCKET_LOG(("STS shutting down thread\n"));
767
768 // detach all sockets, including locals
769 Reset(false);
770
771 // Final pass over the event queue. This makes sure that events posted by
772 // socket detach handlers get processed.
773 NS_ProcessPendingEvents(thread);
774
775 gSocketThread = nullptr;
776
777 psm::StopSSLServerCertVerificationThreads();
778
779 SOCKET_LOG(("STS thread exit\n"));
780 return NS_OK;
781 }
782
783 void
784 nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
785 SocketContext *socketList,
786 int32_t index)
787 {
788 bool isGuarded = false;
789 if (aGuardLocals) {
790 socketList[index].mHandler->IsLocal(&isGuarded);
791 if (!isGuarded)
792 socketList[index].mHandler->KeepWhenOffline(&isGuarded);
793 }
794 if (!isGuarded)
795 DetachSocket(socketList, &socketList[index]);
796 }
797
798 void
799 nsSocketTransportService::Reset(bool aGuardLocals)
800 {
801 // detach any sockets
802 int32_t i;
803 for (i = mActiveCount - 1; i >= 0; --i) {
804 DetachSocketWithGuard(aGuardLocals, mActiveList, i);
805 }
806 for (i = mIdleCount - 1; i >= 0; --i) {
807 DetachSocketWithGuard(aGuardLocals, mIdleList, i);
808 }
809 }
810
811 nsresult
812 nsSocketTransportService::DoPollIteration(bool wait)
813 {
814 SOCKET_LOG(("STS poll iter [%d]\n", wait));
815
816 int32_t i, count;
817
818 //
819 // poll loop
820 //
821 // walk active list backwards to see if any sockets should actually be
822 // idle, then walk the idle list backwards to see if any idle sockets
823 // should become active. take care to check only idle sockets that
824 // were idle to begin with ;-)
825 //
826 count = mIdleCount;
827 for (i=mActiveCount-1; i>=0; --i) {
828 //---
829 SOCKET_LOG((" active [%u] { handler=%p condition=%x pollflags=%hu }\n", i,
830 mActiveList[i].mHandler,
831 mActiveList[i].mHandler->mCondition,
832 mActiveList[i].mHandler->mPollFlags));
833 //---
834 if (NS_FAILED(mActiveList[i].mHandler->mCondition))
835 DetachSocket(mActiveList, &mActiveList[i]);
836 else {
837 uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
838 if (in_flags == 0)
839 MoveToIdleList(&mActiveList[i]);
840 else {
841 // update poll flags
842 mPollList[i+1].in_flags = in_flags;
843 mPollList[i+1].out_flags = 0;
844 }
845 }
846 }
847 for (i=count-1; i>=0; --i) {
848 //---
849 SOCKET_LOG((" idle [%u] { handler=%p condition=%x pollflags=%hu }\n", i,
850 mIdleList[i].mHandler,
851 mIdleList[i].mHandler->mCondition,
852 mIdleList[i].mHandler->mPollFlags));
853 //---
854 if (NS_FAILED(mIdleList[i].mHandler->mCondition))
855 DetachSocket(mIdleList, &mIdleList[i]);
856 else if (mIdleList[i].mHandler->mPollFlags != 0)
857 MoveToPollList(&mIdleList[i]);
858 }
859
860 SOCKET_LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
861
862 #if defined(XP_WIN)
863 // 30 active connections is the historic limit before firefox 7's 256. A few
864 // windows systems have troubles with the higher limit, so actively probe a
865 // limit the first time we exceed 30.
866 if ((mActiveCount > 30) && !mProbedMaxCount)
867 ProbeMaxCount();
868 #endif
869
870 // Measures seconds spent while blocked on PR_Poll
871 uint32_t pollInterval;
872
873 int32_t n = Poll(wait, &pollInterval);
874 if (n < 0) {
875 SOCKET_LOG((" PR_Poll error [%d]\n", PR_GetError()));
876 }
877 else {
878 //
879 // service "active" sockets...
880 //
881 for (i=0; i<int32_t(mActiveCount); ++i) {
882 PRPollDesc &desc = mPollList[i+1];
883 SocketContext &s = mActiveList[i];
884 if (n > 0 && desc.out_flags != 0) {
885 s.mElapsedTime = 0;
886 s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
887 }
888 // check for timeout errors unless disabled...
889 else if (s.mHandler->mPollTimeout != UINT16_MAX) {
890 // update elapsed time counter
891 // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value
892 // here -- otherwise, some compilers will treat it as signed,
893 // which makes them fire signed/unsigned-comparison build
894 // warnings for the comparison against 'pollInterval'.)
895 if (MOZ_UNLIKELY(pollInterval >
896 static_cast<uint32_t>(UINT16_MAX) -
897 s.mElapsedTime))
898 s.mElapsedTime = UINT16_MAX;
899 else
900 s.mElapsedTime += uint16_t(pollInterval);
901 // check for timeout expiration
902 if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
903 s.mElapsedTime = 0;
904 s.mHandler->OnSocketReady(desc.fd, -1);
905 }
906 }
907 }
908
909 //
910 // check for "dead" sockets and remove them (need to do this in
911 // reverse order obviously).
912 //
913 for (i=mActiveCount-1; i>=0; --i) {
914 if (NS_FAILED(mActiveList[i].mHandler->mCondition))
915 DetachSocket(mActiveList, &mActiveList[i]);
916 }
917
918 if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
919 // acknowledge pollable event (wait should not block)
920 if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
921 // On Windows, the TCP loopback connection in the
922 // pollable event may become broken when a laptop
923 // switches between wired and wireless networks or
924 // wakes up from hibernation. We try to create a
925 // new pollable event. If that fails, we fall back
926 // on "busy wait".
927 {
928 MutexAutoLock lock(mLock);
929 PR_DestroyPollableEvent(mThreadEvent);
930 mThreadEvent = PR_NewPollableEvent();
931 }
932 if (!mThreadEvent) {
933 NS_WARNING("running socket transport thread without "
934 "a pollable event");
935 SOCKET_LOG(("running socket transport thread without "
936 "a pollable event"));
937 }
938 mPollList[0].fd = mThreadEvent;
939 // mPollList[0].in_flags was already set to PR_POLL_READ
940 // in Run().
941 mPollList[0].out_flags = 0;
942 }
943 }
944 }
945
946 return NS_OK;
947 }
948
949 nsresult
950 nsSocketTransportService::UpdatePrefs()
951 {
952 mSendBufferSize = 0;
953
954 nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
955 if (tmpPrefService) {
956 int32_t bufferSize;
957 nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
958 if (NS_SUCCEEDED(rv) && bufferSize > 0)
959 mSendBufferSize = bufferSize;
960
961 // Default TCP Keepalive Values.
962 int32_t keepaliveIdleTimeS;
963 rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF,
964 &keepaliveIdleTimeS);
965 if (NS_SUCCEEDED(rv))
966 mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS,
967 1, kMaxTCPKeepIdle);
968
969 int32_t keepaliveRetryIntervalS;
970 rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF,
971 &keepaliveRetryIntervalS);
972 if (NS_SUCCEEDED(rv))
973 mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS,
974 1, kMaxTCPKeepIntvl);
975
976 int32_t keepaliveProbeCount;
977 rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF,
978 &keepaliveProbeCount);
979 if (NS_SUCCEEDED(rv))
980 mKeepaliveProbeCount = clamped(keepaliveProbeCount,
981 1, kMaxTCPKeepCount);
982 bool keepaliveEnabled = false;
983 rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
984 &keepaliveEnabled);
985 if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
986 mKeepaliveEnabledPref = keepaliveEnabled;
987 OnKeepaliveEnabledPrefChange();
988 }
989 }
990
991 return NS_OK;
992 }
993
994 void
995 nsSocketTransportService::OnKeepaliveEnabledPrefChange()
996 {
997 // Dispatch to socket thread if we're not executing there.
998 if (PR_GetCurrentThread() != gSocketThread) {
999 gSocketTransportService->Dispatch(
1000 NS_NewRunnableMethod(
1001 this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
1002 NS_DISPATCH_NORMAL);
1003 return;
1004 }
1005
1006 SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
1007 mKeepaliveEnabledPref ? "enabled" : "disabled"));
1008
1009 // Notify each socket that keepalive has been en/disabled globally.
1010 for (int32_t i = mActiveCount - 1; i >= 0; --i) {
1011 NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
1012 }
1013 for (int32_t i = mIdleCount - 1; i >= 0; --i) {
1014 NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
1015 }
1016 }
1017
1018 void
1019 nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock)
1020 {
1021 MOZ_ASSERT(sock, "SocketContext cannot be null!");
1022 MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
1023
1024 if (!sock || !sock->mHandler) {
1025 return;
1026 }
1027
1028 sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
1029 }
1030
1031 NS_IMETHODIMP
1032 nsSocketTransportService::Observe(nsISupports *subject,
1033 const char *topic,
1034 const char16_t *data)
1035 {
1036 if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
1037 UpdatePrefs();
1038 return NS_OK;
1039 }
1040
1041 if (!strcmp(topic, "profile-initial-state")) {
1042 int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0);
1043 if (blipInterval <= 0) {
1044 return NS_OK;
1045 }
1046
1047 return net::NetworkActivityMonitor::Init(blipInterval);
1048 }
1049
1050 if (!strcmp(topic, "last-pb-context-exited")) {
1051 nsCOMPtr<nsIRunnable> ev =
1052 NS_NewRunnableMethod(this,
1053 &nsSocketTransportService::ClosePrivateConnections);
1054 nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
1055 NS_ENSURE_SUCCESS(rv, rv);
1056 }
1057
1058 return NS_OK;
1059 }
1060
1061 void
1062 nsSocketTransportService::ClosePrivateConnections()
1063 {
1064 // Must be called on the socket thread.
1065 #ifdef DEBUG
1066 bool onSTSThread;
1067 IsOnCurrentThread(&onSTSThread);
1068 MOZ_ASSERT(onSTSThread);
1069 #endif
1070
1071 for (int32_t i = mActiveCount - 1; i >= 0; --i) {
1072 if (mActiveList[i].mHandler->mIsPrivate) {
1073 DetachSocket(mActiveList, &mActiveList[i]);
1074 }
1075 }
1076 for (int32_t i = mIdleCount - 1; i >= 0; --i) {
1077 if (mIdleList[i].mHandler->mIsPrivate) {
1078 DetachSocket(mIdleList, &mIdleList[i]);
1079 }
1080 }
1081
1082 mozilla::ClearPrivateSSLState();
1083 }
1084
1085 NS_IMETHODIMP
1086 nsSocketTransportService::GetSendBufferSize(int32_t *value)
1087 {
1088 *value = mSendBufferSize;
1089 return NS_OK;
1090 }
1091
1092
1093 /// ugly OS specific includes are placed at the bottom of the src for clarity
1094
1095 #if defined(XP_WIN)
1096 #include <windows.h>
1097 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1098 #include <sys/resource.h>
1099 #endif
1100
1101 // Right now the only need to do this is on windows.
1102 #if defined(XP_WIN)
1103 void
1104 nsSocketTransportService::ProbeMaxCount()
1105 {
1106 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1107
1108 if (mProbedMaxCount)
1109 return;
1110 mProbedMaxCount = true;
1111
1112 // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
1113 // sockets. See bug 692260 - windows should be able to handle 1000 sockets
1114 // in select() without a problem, but LSPs have been known to balk at lower
1115 // numbers. (64 in the bug).
1116
1117 // Allocate
1118 struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
1119 uint32_t numAllocated = 0;
1120
1121 for (uint32_t index = 0 ; index < gMaxCount; ++index) {
1122 pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
1123 pfd[index].out_flags = 0;
1124 pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET);
1125 if (!pfd[index].fd) {
1126 SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
1127 if (index < SOCKET_LIMIT_MIN)
1128 gMaxCount = SOCKET_LIMIT_MIN;
1129 else
1130 gMaxCount = index;
1131 break;
1132 }
1133 ++numAllocated;
1134 }
1135
1136 // Test
1137 PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U);
1138 while (gMaxCount <= numAllocated) {
1139 int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
1140
1141 SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n",
1142 gMaxCount, rv));
1143
1144 if (rv >= 0)
1145 break;
1146
1147 SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
1148 gMaxCount, rv, PR_GetError()));
1149
1150 gMaxCount -= 32;
1151 if (gMaxCount <= SOCKET_LIMIT_MIN) {
1152 gMaxCount = SOCKET_LIMIT_MIN;
1153 break;
1154 }
1155 }
1156
1157 // Free
1158 for (uint32_t index = 0 ; index < numAllocated; ++index)
1159 if (pfd[index].fd)
1160 PR_Close(pfd[index].fd);
1161
1162 SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
1163 }
1164 #endif // windows
1165
1166 PRStatus
1167 nsSocketTransportService::DiscoverMaxCount()
1168 {
1169 gMaxCount = SOCKET_LIMIT_MIN;
1170
1171 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1172 // On unix and os x network sockets and file
1173 // descriptors are the same. OS X comes defaulted at 256,
1174 // most linux at 1000. We can reliably use [sg]rlimit to
1175 // query that and raise it. We will try to raise it 250 past
1176 // our target number of SOCKET_LIMIT_TARGET so that some descriptors
1177 // are still available for other things.
1178
1179 struct rlimit rlimitData;
1180 if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1)
1181 return PR_SUCCESS;
1182 if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET + 250) {
1183 gMaxCount = SOCKET_LIMIT_TARGET;
1184 return PR_SUCCESS;
1185 }
1186
1187 int32_t maxallowed = rlimitData.rlim_max;
1188 if (maxallowed == -1) { /* no limit */
1189 maxallowed = SOCKET_LIMIT_TARGET + 250;
1190 } else if ((uint32_t)maxallowed < SOCKET_LIMIT_MIN + 250) {
1191 return PR_SUCCESS;
1192 } else if ((uint32_t)maxallowed > SOCKET_LIMIT_TARGET + 250) {
1193 maxallowed = SOCKET_LIMIT_TARGET + 250;
1194 }
1195
1196 rlimitData.rlim_cur = maxallowed;
1197 setrlimit(RLIMIT_NOFILE, &rlimitData);
1198 if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1)
1199 if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250)
1200 gMaxCount = rlimitData.rlim_cur - 250;
1201
1202 #elif defined(XP_WIN) && !defined(WIN_CE)
1203 // >= XP is confirmed to have at least 1000
1204 gMaxCount = SOCKET_LIMIT_TARGET;
1205 #else
1206 // other platforms are harder to test - so leave at safe legacy value
1207 #endif
1208
1209 return PR_SUCCESS;
1210 }
1211
1212
1213 // Used to return connection info to Dashboard.cpp
1214 void
1215 nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data,
1216 struct SocketContext *context, bool aActive)
1217 {
1218 if (context->mHandler->mIsPrivate)
1219 return;
1220 PRFileDesc *aFD = context->mFD;
1221 bool tcp = (PR_GetDescType(aFD) == PR_DESC_SOCKET_TCP);
1222
1223 PRNetAddr peer_addr;
1224 PR_GetPeerName(aFD, &peer_addr);
1225
1226 char host[64] = {0};
1227 PR_NetAddrToString(&peer_addr, host, sizeof(host));
1228
1229 uint16_t port;
1230 if (peer_addr.raw.family == PR_AF_INET)
1231 port = peer_addr.inet.port;
1232 else
1233 port = peer_addr.ipv6.port;
1234 port = PR_ntohs(port);
1235 uint64_t sent = context->mHandler->ByteCountSent();
1236 uint64_t received = context->mHandler->ByteCountReceived();
1237 SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp };
1238
1239 data->AppendElement(info);
1240 }
1241
1242 void
1243 nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data)
1244 {
1245 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1246 for (uint32_t i = 0; i < mActiveCount; i++)
1247 AnalyzeConnection(data, &mActiveList[i], true);
1248 for (uint32_t i = 0; i < mIdleCount; i++)
1249 AnalyzeConnection(data, &mIdleList[i], false);
1250 }

mercurial