Skip to content

Commit

Permalink
Bug 87717 - Allow offline mode to connect to loopback r=mcmanus sr=cb…
Browse files Browse the repository at this point in the history
…iesinger
  • Loading branch information
hobophobe committed Sep 17, 2012
1 parent a39086b commit b6f4fdb
Show file tree
Hide file tree
Showing 22 changed files with 202 additions and 59 deletions.
11 changes: 10 additions & 1 deletion browser/base/content/test/browser_bug435325.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@

/* Ensure that clicking the button in the Offline mode neterror page makes the browser go online. See bug 435325. */

let proxyPrefValue;

function test() {
waitForExplicitFinish();

gBrowser.selectedTab = gBrowser.addTab();
window.addEventListener("DOMContentLoaded", checkPage, false);

// Go offline and disable the cache, then try to load the test URL.
// Go offline and disable the proxy and cache, then try to load the test URL.
Services.io.offline = true;

// Tests always connect to localhost, and per bug 87717, localhost is now
// reachable in offline mode. To avoid this, disable any proxy.
proxyPrefValue = Services.prefs.getIntPref("network.proxy.type");
Services.prefs.setIntPref("network.proxy.type", 0);

Services.prefs.setBoolPref("browser.cache.disk.enable", false);
Services.prefs.setBoolPref("browser.cache.memory.enable", false);
content.location = "http://example.com/";
Expand Down Expand Up @@ -40,6 +48,7 @@ function checkPage() {
}

registerCleanupFunction(function() {
Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
Services.prefs.setBoolPref("browser.cache.disk.enable", true);
Services.prefs.setBoolPref("browser.cache.memory.enable", true);
Services.io.offline = false;
Expand Down
2 changes: 1 addition & 1 deletion content/base/test/unit/test_error_codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function run_test_pt1() {
}
ioService.offline = true;

gExpectedStatus = Components.results.NS_ERROR_DOCUMENT_NOT_CACHED;
gExpectedStatus = Components.results.NS_ERROR_OFFLINE;
gNextTestFunc = run_test_pt2;
dump("Testing error returned by async XHR when the network is offline\n");
asyncXHR.load();
Expand Down
7 changes: 7 additions & 0 deletions netwerk/base/public/nsASocketHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ class nsASocketHandler : public nsISupports
// by the socket transport service.
//
virtual void OnSocketDetached(PRFileDesc *fd) = 0;

//
// called to determine if the socket is for a local peer.
// when used for server sockets, indicates if it only accepts local
// connections.
//
virtual void IsLocal(bool *aIsLocal) = 0;
};

#endif // !nsASocketHandler_h__
7 changes: 6 additions & 1 deletion netwerk/base/public/nsPISocketTransportService.idl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* This is a private interface used by the internals of the networking library.
* It will never be frozen. Do not use it in external code.
*/
[scriptable, uuid(83123036-81c0-47cb-8d9c-bd85d29a1b3f)]
[scriptable, uuid(32de7b6e-90c3-11e1-b57e-001fbc092072)]

interface nsPISocketTransportService : nsISocketTransportService
{
Expand All @@ -31,4 +31,9 @@ interface nsPISocketTransportService : nsISocketTransportService
*/
readonly attribute long sendBufferSize;

/**
* Controls whether the socket transport service is offline.
* Setting it offline will cause non-local socket detachment.
*/
attribute boolean offline;
};
44 changes: 27 additions & 17 deletions netwerk/base/src/nsIOService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ nsIOService::InitializeSocketTransportService()
rv = mSocketTransportService->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
mSocketTransportService->SetAutodialEnabled(mAutoDialEnabled);
mSocketTransportService->SetOffline(false);
}

return rv;
Expand Down Expand Up @@ -742,41 +743,36 @@ nsIOService::SetOffline(bool offline)
}
}

nsIIOService *subject = static_cast<nsIIOService *>(this);
nsresult rv;
while (mSetOfflineValue != mOffline) {
offline = mSetOfflineValue;

nsresult rv;
if (offline && !mOffline) {
NS_NAMED_LITERAL_STRING(offlineString, NS_IOSERVICE_OFFLINE);
mOffline = true; // indicate we're trying to shutdown

// don't care if notification fails
// this allows users to attempt a little cleanup before dns and socket transport are shut down.
// don't care if notifications fail
if (observerService)
observerService->NotifyObservers(static_cast<nsIIOService *>(this),
observerService->NotifyObservers(subject,
NS_IOSERVICE_GOING_OFFLINE_TOPIC,
offlineString.get());

// be sure to try and shutdown both (even if the first fails)...
// shutdown dns service first, because it has callbacks for socket transport
if (mDNSService) {
rv = mDNSService->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
}
if (mSocketTransportService) {
rv = mSocketTransportService->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
}
if (mDNSService)
mDNSService->SetOffline(true);

if (mSocketTransportService)
mSocketTransportService->SetOffline(true);

// don't care if notification fails
if (observerService)
observerService->NotifyObservers(static_cast<nsIIOService *>(this),
observerService->NotifyObservers(subject,
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
offlineString.get());
}
else if (!offline && mOffline) {
// go online
if (mDNSService) {
mDNSService->SetOffline(false);
rv = mDNSService->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
}
Expand All @@ -790,12 +786,26 @@ nsIOService::SetOffline(bool offline)

// don't care if notification fails
if (observerService)
observerService->NotifyObservers(static_cast<nsIIOService *>(this),
observerService->NotifyObservers(subject,
NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get());
}
}

// Don't notify here, as the above notifications (if used) suffice.
if (mShutdown && mOffline) {
// be sure to try and shutdown both (even if the first fails)...
// shutdown dns service first, because it has callbacks for socket transport
if (mDNSService) {
rv = mDNSService->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
}
if (mSocketTransportService) {
rv = mSocketTransportService->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
}
}

mSettingOffline = false;

return NS_OK;
Expand Down
6 changes: 6 additions & 0 deletions netwerk/base/src/nsServerSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,12 @@ nsServerSocket::OnSocketDetached(PRFileDesc *fd)
}
}

void
nsServerSocket::IsLocal(bool *aIsLocal)
{
// If bound to loopback, this server socket only accepts local connections.
*aIsLocal = PR_IsNetAddrType(&mAddr, PR_IpAddrLoopback);
}

//-----------------------------------------------------------------------------
// nsServerSocket::nsISupports
Expand Down
1 change: 1 addition & 0 deletions netwerk/base/src/nsServerSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class nsServerSocket : public nsASocketHandler
// nsASocketHandler methods:
virtual void OnSocketReady(PRFileDesc *fd, int16_t outFlags);
virtual void OnSocketDetached(PRFileDesc *fd);
virtual void IsLocal(bool *aIsLocal);

nsServerSocket();

Expand Down
13 changes: 13 additions & 0 deletions netwerk/base/src/nsSocketTransport2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,10 @@ nsSocketTransport::InitiateSocket()

nsresult rv;

if (gIOService->IsOffline() &&
!PR_IsNetAddrType(&mNetAddr, PR_IpAddrLoopback))
return NS_ERROR_OFFLINE;

//
// find out if it is going to be ok to attach another socket to the STS.
// if not then we have to wait for the STS to tell us that it is ok.
Expand Down Expand Up @@ -1664,6 +1668,15 @@ nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
}
}

void
nsSocketTransport::IsLocal(bool *aIsLocal)
{
{
MutexAutoLock lock(mLock);
*aIsLocal = PR_IsNetAddrType(&mNetAddr, PR_IpAddrLoopback);
}
}

//-----------------------------------------------------------------------------
// xpcom api

Expand Down
1 change: 1 addition & 0 deletions netwerk/base/src/nsSocketTransport2.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class nsSocketTransport : public nsASocketHandler
// nsASocketHandler methods:
void OnSocketReady(PRFileDesc *, int16_t outFlags);
void OnSocketDetached(PRFileDesc *);
void IsLocal(bool *aIsLocal);

// called when a socket event is handled
void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param);
Expand Down
68 changes: 57 additions & 11 deletions netwerk/base/src/nsSocketTransportService2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ nsSocketTransportService::nsSocketTransportService()
, mLock("nsSocketTransportService::mLock")
, mInitialized(false)
, mShuttingDown(false)
, mOffline(false)
, mGoingOffline(false)
, mActiveListSize(SOCKET_LIMIT_MIN)
, mIdleListSize(SOCKET_LIMIT_MIN)
, mActiveCount(0)
Expand Down Expand Up @@ -427,10 +429,6 @@ nsSocketTransportService::Init()
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();
//
Expand Down Expand Up @@ -516,6 +514,28 @@ nsSocketTransportService::Shutdown()
return NS_OK;
}

NS_IMETHODIMP
nsSocketTransportService::GetOffline(bool *offline)
{
*offline = mOffline;
return NS_OK;
}

NS_IMETHODIMP
nsSocketTransportService::SetOffline(bool offline)
{
if (!mOffline && offline) {
// signal the socket thread to go offline, so it will detach sockets
mGoingOffline = true;
mOffline = true;
}
else if (mOffline && !offline) {
mOffline = false;
}

return NS_OK;
}

NS_IMETHODIMP
nsSocketTransportService::CreateTransport(const char **types,
uint32_t typeCount,
Expand All @@ -524,7 +544,7 @@ nsSocketTransportService::CreateTransport(const char **types,
nsIProxyInfo *proxyInfo,
nsISocketTransport **result)
{
NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);

nsSocketTransport *trans = new nsSocketTransport();
Expand Down Expand Up @@ -624,22 +644,26 @@ nsSocketTransportService::Run()
}
} while (pendingEvents);

bool goingOffline = false;
// now that our event queue is empty, check to see if we should exit
{
MutexAutoLock lock(mLock);
if (mShuttingDown)
break;
if (mGoingOffline) {
mGoingOffline = false;
goingOffline = true;
}
}
// Avoid potential deadlock
if (goingOffline)
Reset(true);
}

SOCKET_LOG(("STS shutting down thread\n"));

// detach any sockets
int32_t i;
for (i=mActiveCount-1; i>=0; --i)
DetachSocket(mActiveList, &mActiveList[i]);
for (i=mIdleCount-1; i>=0; --i)
DetachSocket(mIdleList, &mIdleList[i]);
// detach all sockets, including locals
Reset(false);

// Final pass over the event queue. This makes sure that events posted by
// socket detach handlers get processed.
Expand All @@ -653,6 +677,28 @@ nsSocketTransportService::Run()
return NS_OK;
}

void
nsSocketTransportService::Reset(bool aGuardLocals)
{
// detach any sockets
int32_t i;
bool isGuarded;
for (i = mActiveCount - 1; i >= 0; --i) {
isGuarded = false;
if (aGuardLocals)
mActiveList[i].mHandler->IsLocal(&isGuarded);
if (!isGuarded)
DetachSocket(mActiveList, &mActiveList[i]);
}
for (i = mIdleCount - 1; i >= 0; --i) {
isGuarded = false;
if (aGuardLocals)
mIdleList[i].mHandler->IsLocal(&isGuarded);
if (!isGuarded)
DetachSocket(mIdleList, &mIdleList[i]);
}
}

nsresult
nsSocketTransportService::DoPollIteration(bool wait)
{
Expand Down
5 changes: 5 additions & 0 deletions netwerk/base/src/nsSocketTransportService2.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ class nsSocketTransportService : public nsPISocketTransportService
bool mShuttingDown;
// indicates whether we are currently in the
// process of shutting down
bool mOffline;
bool mGoingOffline;

// Detaches all sockets.
void Reset(bool aGuardLocals);

//-------------------------------------------------------------------------
// socket lists (socket thread only)
Expand Down
Loading

0 comments on commit b6f4fdb

Please sign in to comment.