|
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 } |