Bug 614286 - Don't initialize the socket transport service unless we know that we're online; r=bzbarsky a=blocking-beta8+
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -169,48 +169,38 @@ static const char kProfileChangeNetResto
// Necko buffer cache
nsIMemory* nsIOService::gBufferCache = nsnull;
PRUint32 nsIOService::gDefaultSegmentSize = 4096;
PRUint32 nsIOService::gDefaultSegmentCount = 24;
////////////////////////////////////////////////////////////////////////////////
nsIOService::nsIOService()
- : mOffline(PR_FALSE)
+ : mOffline(PR_TRUE)
, mOfflineForProfileChange(PR_FALSE)
, mManageOfflineStatus(PR_TRUE)
, mSettingOffline(PR_FALSE)
, mSetOfflineValue(PR_FALSE)
, mShutdown(PR_FALSE)
, mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
, mContentSniffers(NS_CONTENT_SNIFFER_CATEGORY)
{
}
nsresult
nsIOService::Init()
{
NS_TIME_FUNCTION;
nsresult rv;
-
- // We need to get references to these services so that we can shut them
+
+ // We need to get references to the DNS service so that we can shut it
// down later. If we wait until the nsIOService is being shut down,
// GetService will fail at that point.
- // TODO(darin): Load the Socket and DNS services lazily.
-
- mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
- if (NS_FAILED(rv)) {
- NS_WARNING("failed to get socket transport service");
- return rv;
- }
-
- NS_TIME_FUNCTION_MARK("got SocketTransportService");
-
mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
NS_WARNING("failed to get DNS service");
return rv;
}
NS_TIME_FUNCTION_MARK("got DNS Service");
@@ -288,17 +278,39 @@ nsIOService::Init()
return NS_OK;
}
nsIOService::~nsIOService()
{
gIOService = nsnull;
-}
+}
+
+nsresult
+nsIOService::InitializeSocketTransportService()
+{
+ NS_TIME_FUNCTION;
+
+ nsresult rv = NS_OK;
+
+ if (!mSocketTransportService) {
+ mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to get socket transport service");
+ }
+ }
+
+ if (mSocketTransportService) {
+ rv = mSocketTransportService->Init();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
+ }
+
+ return rv;
+}
nsIOService*
nsIOService::GetInstance() {
if (!gIOService) {
gIOService = new nsIOService();
if (!gIOService)
return nsnull;
NS_ADDREF(gIOService);
@@ -737,20 +749,17 @@ nsIOService::SetOffline(PRBool offline)
offlineString.get());
}
else if (!offline && mOffline) {
// go online
if (mDNSService) {
rv = mDNSService->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
}
- if (mSocketTransportService) {
- rv = mSocketTransportService->Init();
- NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
- }
+ InitializeSocketTransportService();
mOffline = PR_FALSE; // indicate success only AFTER we've
// brought up the services
// trigger a PAC reload when we come back online
if (mProxyService)
mProxyService->ReloadPAC();
// don't care if notification fails
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -103,16 +103,20 @@ public:
// Gets the array of registered content sniffers
const nsCOMArray<nsIContentSniffer>& GetContentSniffers() {
return mContentSniffers.GetEntries();
}
PRBool IsOffline() { return mOffline; }
PRBool IsLinkUp();
+ PRBool IsComingOnline() const {
+ return mOffline && mSettingOffline && !mSetOfflineValue;
+ }
+
private:
// These shouldn't be called directly:
// - construct using GetInstance
// - destroy using Release
nsIOService() NS_HIDDEN;
~nsIOService() NS_HIDDEN;
NS_HIDDEN_(nsresult) TrackNetworkLinkStatusForOffline();
@@ -124,16 +128,18 @@ private:
NS_HIDDEN_(nsresult) CacheProtocolHandler(const char *scheme,
nsIProtocolHandler* hdlr);
// Prefs wrangling
NS_HIDDEN_(void) PrefsChanged(nsIPrefBranch *prefs, const char *pref = nsnull);
NS_HIDDEN_(void) GetPrefBranch(nsIPrefBranch2 **);
NS_HIDDEN_(void) ParsePortList(nsIPrefBranch *prefBranch, const char *pref, PRBool remove);
+ nsresult InitializeSocketTransportService();
+
private:
PRPackedBool mOffline;
PRPackedBool mOfflineForProfileChange;
PRPackedBool mManageOfflineStatus;
// Used to handle SetOffline() reentrancy. See the comment in
// SetOffline() for more details.
PRPackedBool mSettingOffline;
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -47,16 +47,17 @@
#include "nsNetError.h"
#include "prnetdb.h"
#include "prlock.h"
#include "prerror.h"
#include "plstr.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch2.h"
#include "nsServiceManagerUtils.h"
+#include "nsIOService.h"
#include "mozilla/FunctionTimer.h"
#if defined(PR_LOGGING)
PRLogModuleInfo *gSocketTransportLog = nsnull;
#endif
nsSocketTransportService *gSocketTransportService = nsnull;
@@ -390,16 +391,20 @@ nsSocketTransportService::Init()
}
if (mInitialized)
return NS_OK;
if (mShuttingDown)
return NS_ERROR_UNEXPECTED;
+ // Don't initialize inside the offline mode
+ if (gIOService->IsOffline() && !gIOService->IsComingOnline())
+ return NS_ERROR_OFFLINE;
+
if (!mThreadEvent) {
mThreadEvent = PR_NewPollableEvent();
//
// NOTE: per bug 190000, this failure could be caused by Zone-Alarm
// or similar software.
//
// NOTE: per bug 191739, this failure could also be caused by lack
// of a loopback device on Windows and OS/2 platforms (NSPR creates
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -52,16 +52,17 @@
#include "nsNetError.h"
#include "nsDNSPrefetch.h"
#include "nsIProtocolProxyService.h"
#include "prsystem.h"
#include "prnetdb.h"
#include "prmon.h"
#include "prio.h"
#include "plstr.h"
+#include "nsIOService.h"
#include "mozilla/FunctionTimer.h"
static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
static const char kPrefEnableIDN[] = "network.enableIDN";
static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
@@ -377,33 +378,38 @@ nsDNSService::Init()
}
// we have to null out mIDN since we might be getting re-initialized
// as a result of a pref change.
nsCOMPtr<nsIIDNService> idn;
if (enableIDN)
idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
+ nsDNSPrefetch::Initialize(this);
+
+ // Don't initialize the resolver if we're in offline mode.
+ // Later on, the IO service will reinitialize us when going online.
+ if (gIOService->IsOffline() && !gIOService->IsComingOnline())
+ return NS_OK;
+
nsRefPtr<nsHostResolver> res;
nsresult rv = nsHostResolver::Create(maxCacheEntries,
maxCacheLifetime,
getter_AddRefs(res));
if (NS_SUCCEEDED(rv)) {
// now, set all of our member variables while holding the lock
nsAutoLock lock(mLock);
mResolver = res;
mIDN = idn;
mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
mDisableIPv6 = disableIPv6;
// Disable prefetching either by explicit preference or if a manual proxy is configured
mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
}
-
- nsDNSPrefetch::Initialize(this);
return rv;
}
NS_IMETHODIMP
nsDNSService::Shutdown()
{
nsRefPtr<nsHostResolver> res;
{
@@ -566,18 +572,18 @@ nsDNSService::Observe(nsISupports *subje
//
// NOTE Shutting down and reinitializing the service like this is obviously
// suboptimal if Observe gets called several times in a row, but we don't
// expect that to be the case.
//
if (mResolver) {
Shutdown();
- Init();
}
+ Init();
return NS_OK;
}
PRUint16
nsDNSService::GetAFForLookup(const nsACString &host)
{
if (mDisableIPv6)
return PR_AF_INET;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -38,16 +38,17 @@
#include "nsHttpConnectionMgr.h"
#include "nsHttpConnection.h"
#include "nsHttpPipeline.h"
#include "nsHttpHandler.h"
#include "nsAutoLock.h"
#include "nsNetCID.h"
#include "nsCOMPtr.h"
+#include "nsNetUtil.h"
#include "nsIServiceManager.h"
#include "nsIObserverService.h"
// defined by the socket transport service while active
extern PRThread *gSocketThread;
@@ -96,48 +97,65 @@ nsHttpConnectionMgr::~nsHttpConnectionMg
{
LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
if (mMonitor)
nsAutoMonitor::DestroyMonitor(mMonitor);
}
nsresult
+nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
+{
+ nsresult rv;
+ nsCOMPtr<nsIEventTarget> sts;
+ nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+ if (NS_SUCCEEDED(rv)) {
+ PRBool offline = PR_TRUE;
+ ioService->GetOffline(&offline);
+
+ if (!offline) {
+ sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ }
+ }
+
+ nsAutoMonitor mon(mMonitor);
+
+ // do nothing if already initialized
+ if (mSocketThreadTarget)
+ return NS_OK;
+
+ mSocketThreadTarget = sts;
+
+ return rv;
+}
+
+nsresult
nsHttpConnectionMgr::Init(PRUint16 maxConns,
PRUint16 maxConnsPerHost,
PRUint16 maxConnsPerProxy,
PRUint16 maxPersistConnsPerHost,
PRUint16 maxPersistConnsPerProxy,
PRUint16 maxRequestDelay,
PRUint16 maxPipelinedRequests)
{
LOG(("nsHttpConnectionMgr::Init\n"));
- nsresult rv;
- nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
- if (NS_FAILED(rv)) return rv;
-
- nsAutoMonitor mon(mMonitor);
-
- // do nothing if already initialized
- if (mSocketThreadTarget)
- return NS_OK;
+ {
+ nsAutoMonitor mon(mMonitor);
- // no need to do any special synchronization here since there cannot be
- // any activity on the socket thread (because Shutdown is synchronous).
- mMaxConns = maxConns;
- mMaxConnsPerHost = maxConnsPerHost;
- mMaxConnsPerProxy = maxConnsPerProxy;
- mMaxPersistConnsPerHost = maxPersistConnsPerHost;
- mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
- mMaxRequestDelay = maxRequestDelay;
- mMaxPipelinedRequests = maxPipelinedRequests;
+ mMaxConns = maxConns;
+ mMaxConnsPerHost = maxConnsPerHost;
+ mMaxConnsPerProxy = maxConnsPerProxy;
+ mMaxPersistConnsPerHost = maxPersistConnsPerHost;
+ mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
+ mMaxRequestDelay = maxRequestDelay;
+ mMaxPipelinedRequests = maxPipelinedRequests;
+ }
- mSocketThreadTarget = sts;
- return rv;
+ return EnsureSocketThreadTargetIfOnline();
}
nsresult
nsHttpConnectionMgr::Shutdown()
{
LOG(("nsHttpConnectionMgr::Shutdown\n"));
nsAutoMonitor mon(mMonitor);
@@ -161,16 +179,22 @@ nsHttpConnectionMgr::Shutdown()
// wait for shutdown event to complete
mon.Wait();
return NS_OK;
}
nsresult
nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
{
+ // This object doesn't get reinitialized if the offline state changes, so our
+ // socket thread target might be uninitialized if we were offline when this
+ // object was being initialized, and we go online later on. This call takes
+ // care of initializing the socket thread target if that's the case.
+ EnsureSocketThreadTargetIfOnline();
+
nsAutoMonitor mon(mMonitor);
nsresult rv;
if (!mSocketThreadTarget) {
NS_WARNING("cannot post event if not initialized");
rv = NS_ERROR_NOT_INITIALIZED;
}
else {
@@ -280,16 +304,22 @@ nsresult
nsHttpConnectionMgr::PruneDeadConnections()
{
return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
}
nsresult
nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
{
+ // This object doesn't get reinitialized if the offline state changes, so our
+ // socket thread target might be uninitialized if we were offline when this
+ // object was being initialized, and we go online later on. This call takes
+ // care of initializing the socket thread target if that's the case.
+ EnsureSocketThreadTargetIfOnline();
+
nsAutoMonitor mon(mMonitor);
NS_IF_ADDREF(*target = mSocketThreadTarget);
return NS_OK;
}
void
nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
{
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -210,16 +210,17 @@ private:
static PRIntn PurgeExcessIdleConnectionsCB(nsHashKey *, void *, void *);
PRBool ProcessPendingQForEntry(nsConnectionEntry *);
PRBool AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
void GetConnection(nsConnectionEntry *, PRUint8 caps, nsHttpConnection **);
nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *,
PRUint8 caps, nsHttpConnection *);
PRBool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
nsresult ProcessNewTransaction(nsHttpTransaction *);
+ nsresult EnsureSocketThreadTargetIfOnline();
// message handlers have this signature
typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(PRInt32, void *);
// nsConnEvent
//
// subclass of nsRunnable used to marshall events to the socket transport
// thread. this class is used to implement PostEvent.
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_offline_status.js
@@ -0,0 +1,10 @@
+function run_test() {
+ var ioService = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+ var linkService = Components.classes["@mozilla.org/network/network-link-service;1"]
+ .getService(Components.interfaces.nsINetworkLinkService);
+
+ // The offline status should depends on the link status
+ do_check_neq(ioService.offline, linkService.isLinkUp);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_sockettransportsvc_available.js
@@ -0,0 +1,8 @@
+function run_test() {
+ try {
+ var sts = Components.classes["@mozilla.org/network/socket-transport-service;1"]
+ .getService(Components.interfaces.nsISocketTransportService);
+ } catch(e) {}
+
+ do_check_true(!!sts);
+}