Bug 783205 - Networking Dashboard. r=mcmanus, r=jorendorff, sr=biesi

This commit is contained in:
Valentin Gosu 2012-12-10 09:13:55 -05:00
parent 2ca324a589
commit bd752af443
29 changed files with 971 additions and 2 deletions

View File

@ -75,6 +75,8 @@ public:
virtual void OnSocketReady(PRFileDesc *fd, int16_t outflags);
virtual void OnSocketDetached(PRFileDesc *fd);
virtual void IsLocal(bool *aIsLocal);
virtual uint64_t ByteCountSent() { return 0; }
virtual uint64_t ByteCountReceived() { return 0; }
// nsISupports methods
NS_DECL_ISUPPORTS

View File

@ -129,6 +129,9 @@ class SocketHandler : public nsASocketHandler {
*aIsLocal = false;
}
virtual uint64_t ByteCountSent() { return 0; }
virtual uint64_t ByteCountReceived() { return 0; }
NS_DECL_ISUPPORTS
private:

View File

@ -82,6 +82,9 @@ class TransportLayerPrsock : public TransportLayer {
*aIsLocal = false;
}
virtual uint64_t ByteCountSent() { return 0; }
virtual uint64_t ByteCountReceived() { return 0; }
// nsISupports methods
NS_DECL_ISUPPORTS

View File

@ -30,6 +30,8 @@ SDK_XPIDLSRCS = \
$(NULL)
XPIDLSRCS = \
nsIDashboard.idl \
nsIDashboardEventNotifier.idl \
nsIApplicationCache.idl \
nsIApplicationCacheChannel.idl \
nsIApplicationCacheContainer.idl \

View File

@ -67,6 +67,13 @@ public:
// connections.
//
virtual void IsLocal(bool *aIsLocal) = 0;
//
// returns the number of bytes sent/transmitted over the socket
//
virtual uint64_t ByteCountSent() = 0;
virtual uint64_t ByteCountReceived() = 0;
};
#endif // !nsASocketHandler_h__

View File

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIDashboardEventNotifier.idl"
/* A JavaScript callback function that takes a JSON as its parameter.
* The returned JSON contains arrays with data
*/
[scriptable, function, uuid(19d7f24f-a95a-4fd9-87e2-d96e9e4b1f6d)]
interface NetDashboardCallback : nsISupports
{
void onDashboardDataAvailable(in jsval data);
};
/* The dashboard service.
* The async API returns JSONs, which hold arrays with the required info.
* Only one request of each type may be pending at any time.
*/
[scriptable, uuid(c79eb3c6-091a-45a6-8544-5a8d1ab79537)]
interface nsIDashboard : nsISupports
{
/* Arrays: host, port, tcp, active, socksent, sockreceived
* Values: sent, received */
void requestSockets(in NetDashboardCallback cb);
/* Arrays: host, port, spdy, ssl
* Array of arrays: active, idle */
void requestHttpConnections(in NetDashboardCallback cb);
/* Arrays: hostport, encrypted, msgsent, msgreceived, sentsize, receivedsize */
void requestWebsocketConnections(in NetDashboardCallback cb);
/* Arrays: hostname, family, hostaddr, expiration */
void requestDNSInfo(in NetDashboardCallback cb);
/* When true, the service will log websocket events */
attribute boolean enableLogging;
};

View File

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
[builtinclass, uuid(24fdfcbe-54cb-4997-8392-3c476126ea3b)]
interface nsIDashboardEventNotifier : nsISupports
{
/* These methods are called to register a websocket event with the dashboard
*
* A host is identified by the (aHost, aSerial) pair.
* aHost: the host's name: example.com
* aSerial: a number that uniquely identifies the websocket
*
* aEncrypted: if the connection is encrypted
* aLength: the length of the message in bytes
*/
void addHost(in ACString aHost, in unsigned long aSerial, in boolean aEncrypted);
void removeHost(in ACString aHost, in unsigned long aSerial);
void newMsgSent(in ACString aHost, in unsigned long aSerial, in unsigned long aLength);
void newMsgReceived(in ACString aHost, in unsigned long aSerial, in unsigned long aLength);
};

View File

@ -0,0 +1,415 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http:mozilla.org/MPL/2.0/. */
#include "nsContentUtils.h"
#include "mozilla/net/Dashboard.h"
#include "mozilla/net/HttpInfo.h"
namespace mozilla {
namespace net {
NS_IMPL_THREADSAFE_ISUPPORTS2(Dashboard, nsIDashboard, nsIDashboardEventNotifier)
#define CREATE_ARRAY_OBJECT(object) \
JSObject* object = JS_NewArrayObject(cx, 0, nullptr); \
if (!object) \
return NS_ERROR_OUT_OF_MEMORY ; \
#define SET_ELEMENT(object, func, param, index) \
if (!JS_DefineElement(cx, object, index, func(param), \
nullptr, nullptr, JSPROP_ENUMERATE)) \
return NS_ERROR_OUT_OF_MEMORY; \
#define SET_PROPERTY(finalObject, object, property) \
val = OBJECT_TO_JSVAL(object); \
if (!JS_DefineProperty(cx, finalObject, #property, \
val, nullptr, nullptr, JSPROP_ENUMERATE)) \
return NS_ERROR_OUT_OF_MEMORY; \
Dashboard::Dashboard()
{
mEnableLogging = false;
}
Dashboard::~Dashboard()
{
}
NS_IMETHODIMP
Dashboard::RequestSockets(NetDashboardCallback* cb)
{
if (mSock.cb)
return NS_ERROR_FAILURE;
mSock.cb = cb;
mSock.data.Clear();
mSock.thread = NS_GetCurrentThread();
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetSocketsDispatch);
gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
void
Dashboard::GetSocketsDispatch()
{
if (gSocketTransportService)
gSocketTransportService->GetSocketConnections(&mSock.data);
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetSockets);
mSock.thread->Dispatch(event, NS_DISPATCH_NORMAL);
}
nsresult
Dashboard::GetSockets()
{
jsval val;
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSAutoRequest request(cx);
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
if (!finalObject)
return NS_ERROR_OUT_OF_MEMORY;
CREATE_ARRAY_OBJECT(hostJs);
CREATE_ARRAY_OBJECT(portJs);
CREATE_ARRAY_OBJECT(activeJs);
CREATE_ARRAY_OBJECT(sentJs);
CREATE_ARRAY_OBJECT(receivedJs);
CREATE_ARRAY_OBJECT(tcpJs);
CREATE_ARRAY_OBJECT(sockSentJs);
CREATE_ARRAY_OBJECT(sockRecJs);
mSock.totalSent = 0;
mSock.totalRecv = 0;
for (uint32_t i = 0; i < mSock.data.Length(); i++) {
JSString* hostString = JS_NewStringCopyZ(cx, mSock.data[i].host.get());
SET_ELEMENT(hostJs, STRING_TO_JSVAL, hostString, i);
SET_ELEMENT(portJs, INT_TO_JSVAL, mSock.data[i].port, i);
SET_ELEMENT(activeJs, BOOLEAN_TO_JSVAL, mSock.data[i].active, i);
SET_ELEMENT(tcpJs, INT_TO_JSVAL, mSock.data[i].tcp, i);
SET_ELEMENT(sockSentJs, DOUBLE_TO_JSVAL, (double) mSock.data[i].sent, i);
SET_ELEMENT(sockRecJs, DOUBLE_TO_JSVAL, (double) mSock.data[i].received, i);
mSock.totalSent += mSock.data[i].sent;
mSock.totalRecv += mSock.data[i].received;
}
SET_ELEMENT(sentJs, DOUBLE_TO_JSVAL, (double) mSock.totalSent, 0);
SET_ELEMENT(receivedJs, DOUBLE_TO_JSVAL, (double) mSock.totalRecv, 0);
SET_PROPERTY(finalObject, hostJs, host);
SET_PROPERTY(finalObject, portJs, port);
SET_PROPERTY(finalObject, activeJs, active);
SET_PROPERTY(finalObject, tcpJs, tcp);
SET_PROPERTY(finalObject, sockSentJs, socksent);
SET_PROPERTY(finalObject, sockRecJs, sockreceived);
SET_PROPERTY(finalObject, sentJs, sent);
SET_PROPERTY(finalObject, receivedJs, received);
val = OBJECT_TO_JSVAL(finalObject);
mSock.cb->OnDashboardDataAvailable(val);
mSock.cb = nullptr;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::RequestHttpConnections(NetDashboardCallback* cb)
{
if (mHttp.cb)
return NS_ERROR_FAILURE;
mHttp.cb = cb;
mHttp.data.Clear();
mHttp.thread = NS_GetCurrentThread();
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetHttpDispatch);
gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
void
Dashboard::GetHttpDispatch()
{
HttpInfo::GetHttpConnectionData(&mHttp.data);
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetHttpConnections);
mHttp.thread->Dispatch(event, NS_DISPATCH_NORMAL);
}
nsresult
Dashboard::GetHttpConnections()
{
jsval val;
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSAutoRequest request(cx);
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
if (!finalObject)
return NS_ERROR_OUT_OF_MEMORY;
CREATE_ARRAY_OBJECT(hostJs);
CREATE_ARRAY_OBJECT(portJs);
CREATE_ARRAY_OBJECT(activeJs);
CREATE_ARRAY_OBJECT(idleJs);
CREATE_ARRAY_OBJECT(spdyJs);
CREATE_ARRAY_OBJECT(sslJs);
for (uint32_t i = 0; i < mHttp.data.Length(); i++) {
JSString* hostString = JS_NewStringCopyZ(cx, mHttp.data[i].host.get());
SET_ELEMENT(hostJs, STRING_TO_JSVAL, hostString, i);
SET_ELEMENT(portJs, INT_TO_JSVAL, mHttp.data[i].port, i);
JSObject* rtt_Active = JS_NewArrayObject(cx, 0, nullptr);
JSObject* timeToLive_Active = JS_NewArrayObject(cx, 0, nullptr);
for (uint32_t j = 0; j < mHttp.data[i].active.Length(); j++) {
SET_ELEMENT(rtt_Active, INT_TO_JSVAL, mHttp.data[i].active[j].rtt, j);
SET_ELEMENT(timeToLive_Active, INT_TO_JSVAL, mHttp.data[i].active[j].ttl, j);
}
JSObject* active = JS_NewObject(cx, nullptr, nullptr, nullptr);
SET_PROPERTY(active, rtt_Active, rtt);
SET_PROPERTY(active, timeToLive_Active, ttl);
SET_ELEMENT(activeJs, OBJECT_TO_JSVAL, active, i);
JSObject* rtt_Idle = JS_NewArrayObject(cx, 0, nullptr);
JSObject* timeToLive_Idle = JS_NewArrayObject(cx, 0, nullptr);
for (uint32_t j = 0; j < mHttp.data[i].idle.Length(); j++) {
SET_ELEMENT(rtt_Idle, INT_TO_JSVAL, mHttp.data[i].idle[j].rtt, j);
SET_ELEMENT(timeToLive_Idle, INT_TO_JSVAL, mHttp.data[i].idle[j].ttl, j);
}
JSObject* idle = JS_NewObject(cx, nullptr, nullptr, nullptr);
SET_PROPERTY(idle, rtt_Idle, rtt);
SET_PROPERTY(idle, timeToLive_Idle, ttl);
SET_ELEMENT(idleJs, OBJECT_TO_JSVAL, idle, i);
SET_ELEMENT(spdyJs, BOOLEAN_TO_JSVAL, mHttp.data[i].spdy, i);
SET_ELEMENT(sslJs, BOOLEAN_TO_JSVAL, mHttp.data[i].ssl, i);
}
SET_PROPERTY(finalObject, hostJs, host);
SET_PROPERTY(finalObject, portJs, port);
SET_PROPERTY(finalObject, activeJs, active);
SET_PROPERTY(finalObject, idleJs, idle);
SET_PROPERTY(finalObject, spdyJs, spdy);
SET_PROPERTY(finalObject, sslJs, ssl);
val = OBJECT_TO_JSVAL(finalObject);
mHttp.cb->OnDashboardDataAvailable(val);
mHttp.cb = nullptr;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::GetEnableLogging(bool* value)
{
*value = mEnableLogging;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::SetEnableLogging(const bool value)
{
mEnableLogging = value;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::AddHost(const nsACString& aHost, uint32_t aSerial, bool aEncrypted)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
LogData data(nsCString(aHost), aSerial, aEncrypted);
if (mWs.data.Contains(data))
return NS_OK;
if (!mWs.data.AppendElement(data))
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::RemoveHost(const nsACString& aHost, uint32_t aSerial)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
if (index == -1)
return NS_ERROR_FAILURE;
mWs.data.RemoveElementAt(index);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::NewMsgSent(const nsACString& aHost, uint32_t aSerial, uint32_t aLength)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
if (index == -1)
return NS_ERROR_FAILURE;
mWs.data[index].mMsgSent++;
mWs.data[index].mSizeSent += aLength;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::NewMsgReceived(const nsACString& aHost, uint32_t aSerial, uint32_t aLength)
{
if (mEnableLogging) {
mozilla::MutexAutoLock lock(mWs.lock);
int32_t index = mWs.IndexOf(nsCString(aHost), aSerial);
if (index == -1)
return NS_ERROR_FAILURE;
mWs.data[index].mMsgReceived++;
mWs.data[index].mSizeReceived += aLength;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
Dashboard::RequestWebsocketConnections(NetDashboardCallback* cb)
{
if (mWs.cb)
return NS_ERROR_FAILURE;
mWs.cb = cb;
mWs.thread = NS_GetCurrentThread();
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetWebSocketConnections);
mWs.thread->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
Dashboard::GetWebSocketConnections()
{
jsval val;
JSString* jsstring;
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSAutoRequest request(cx);
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
if (!finalObject)
return NS_ERROR_OUT_OF_MEMORY;
CREATE_ARRAY_OBJECT(hostJs);
CREATE_ARRAY_OBJECT(msgSentJs);
CREATE_ARRAY_OBJECT(msgRecvJs);
CREATE_ARRAY_OBJECT(sizeSentJs);
CREATE_ARRAY_OBJECT(sizeRecvJs);
CREATE_ARRAY_OBJECT(encryptJs);
mozilla::MutexAutoLock lock(mWs.lock);
for (uint32_t i = 0; i < mWs.data.Length(); i++) {
jsstring = JS_NewStringCopyN(cx, mWs.data[i].mHost.get(), mWs.data[i].mHost.Length());
SET_ELEMENT(hostJs, STRING_TO_JSVAL, jsstring, i);
SET_ELEMENT(msgSentJs, INT_TO_JSVAL, mWs.data[i].mMsgSent, i);
SET_ELEMENT(msgRecvJs, INT_TO_JSVAL, mWs.data[i].mMsgReceived, i);
SET_ELEMENT(sizeSentJs, DOUBLE_TO_JSVAL, (double) mWs.data[i].mSizeSent, i);
SET_ELEMENT(sizeRecvJs, DOUBLE_TO_JSVAL, (double) mWs.data[i].mSizeReceived, i);
SET_ELEMENT(encryptJs, BOOLEAN_TO_JSVAL, mWs.data[i].mEncrypted, i);
}
SET_PROPERTY(finalObject, hostJs, hostport);
SET_PROPERTY(finalObject, msgSentJs, msgsent);
SET_PROPERTY(finalObject, msgRecvJs, msgreceived);
SET_PROPERTY(finalObject, sizeSentJs, sentsize);
SET_PROPERTY(finalObject, sizeRecvJs, receivedsize);
SET_PROPERTY(finalObject, encryptJs, encrypted);
val = OBJECT_TO_JSVAL(finalObject);
mWs.cb->OnDashboardDataAvailable(val);
mWs.cb = nullptr;
return NS_OK;
}
NS_IMETHODIMP
Dashboard::RequestDNSInfo(NetDashboardCallback* cb)
{
if (mDns.cb)
return NS_ERROR_FAILURE;
mDns.cb = cb;
nsresult rv;
mDns.data.Clear();
mDns.thread = NS_GetCurrentThread();
if (!mDns.serv) {
mDns.serv = do_GetService("@mozilla.org/network/dns-service;1", &rv);
if (NS_FAILED(rv))
return rv;
}
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetDnsInfoDispatch);
gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
void
Dashboard::GetDnsInfoDispatch()
{
if (mDns.serv)
mDns.serv->GetDNSCacheEntries(&mDns.data);
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &Dashboard::GetDNSCacheEntries);
mDns.thread->Dispatch(event, NS_DISPATCH_NORMAL);
}
nsresult
Dashboard::GetDNSCacheEntries()
{
jsval val;
JSContext* cx = nsContentUtils::GetSafeJSContext();
JSAutoRequest request(cx);
JSObject* finalObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
if (!finalObject)
return NS_ERROR_OUT_OF_MEMORY;
CREATE_ARRAY_OBJECT(nameJs);
CREATE_ARRAY_OBJECT(addrJs);
CREATE_ARRAY_OBJECT(familyJs);
CREATE_ARRAY_OBJECT(expiresJs);
for (uint32_t i = 0; i < mDns.data.Length(); i++) {
JSString* hostnameString = JS_NewStringCopyZ(cx, mDns.data[i].hostname.get());
SET_ELEMENT(nameJs, STRING_TO_JSVAL, hostnameString, i);
JSObject* addrObject = JS_NewObject(cx, nullptr, nullptr, nullptr);
if (!addrObject)
return NS_ERROR_OUT_OF_MEMORY;
for (uint32_t j = 0; j < mDns.data[i].hostaddr.Length(); j++) {
JSString* addrString = JS_NewStringCopyZ(cx, mDns.data[i].hostaddr[j].get());
SET_ELEMENT(addrObject, STRING_TO_JSVAL, addrString, j);
}
SET_ELEMENT(addrJs, OBJECT_TO_JSVAL, addrObject, i);
JSString* familyString;
if (mDns.data[i].family == PR_AF_INET6)
familyString = JS_NewStringCopyZ(cx, "ipv6");
else
familyString = JS_NewStringCopyZ(cx, "ipv4");
SET_ELEMENT(familyJs, STRING_TO_JSVAL, familyString, i);
SET_ELEMENT(expiresJs, DOUBLE_TO_JSVAL, (double) mDns.data[i].expiration, i);
}
SET_PROPERTY(finalObject, nameJs, hostname);
SET_PROPERTY(finalObject, addrJs, hostaddr);
SET_PROPERTY(finalObject, familyJs, family);
SET_PROPERTY(finalObject, expiresJs, expiration);
val = OBJECT_TO_JSVAL(finalObject);
mDns.cb->OnDashboardDataAvailable(val);
mDns.cb = nullptr;
return NS_OK;
}
} } // namespace mozilla::net

View File

@ -0,0 +1,119 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDashboard_h__
#define nsDashboard_h__
#include "nsIDashboard.h"
#include "nsIDashboardEventNotifier.h"
#include "nsTArray.h"
#include "nsString.h"
#include "jsapi.h"
#include "nsIDNSService.h"
#include "nsIServiceManager.h"
#include "nsIThread.h"
#include "nsSocketTransport2.h"
#include "mozilla/net/DashboardTypes.h"
namespace mozilla {
namespace net {
class Dashboard:
public nsIDashboard,
public nsIDashboardEventNotifier
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDASHBOARD
NS_DECL_NSIDASHBOARDEVENTNOTIFIER
Dashboard();
private:
virtual ~Dashboard();
void GetSocketsDispatch();
void GetHttpDispatch();
void GetDnsInfoDispatch();
/* Helper methods that pass the JSON to the callback function. */
nsresult GetSockets();
nsresult GetHttpConnections();
nsresult GetWebSocketConnections();
nsresult GetDNSCacheEntries();
private:
struct SocketData
{
uint64_t totalSent;
uint64_t totalRecv;
nsTArray<SocketInfo> data;
nsCOMPtr<NetDashboardCallback> cb;
nsIThread* thread;
};
struct HttpData {
nsTArray<HttpRetParams> data;
nsCOMPtr<NetDashboardCallback> cb;
nsIThread* thread;
};
struct LogData
{
LogData(nsCString host, uint32_t serial, bool encryption):
mHost(host),
mSerial(serial),
mMsgSent(0),
mMsgReceived(0),
mSizeSent(0),
mSizeReceived(0),
mEncrypted(encryption)
{ }
nsCString mHost;
uint32_t mSerial;
uint32_t mMsgSent;
uint32_t mMsgReceived;
uint64_t mSizeSent;
uint64_t mSizeReceived;
bool mEncrypted;
bool operator==(const LogData& a) const
{
return mHost.Equals(a.mHost) && (mSerial == a.mSerial);
}
};
struct WebSocketData
{
WebSocketData():lock("Dashboard.webSocketData")
{
}
uint32_t IndexOf(nsCString hostname, uint32_t mSerial)
{
LogData temp(hostname, mSerial, false);
return data.IndexOf(temp);
}
nsTArray<LogData> data;
mozilla::Mutex lock;
nsCOMPtr<NetDashboardCallback> cb;
nsIThread* thread;
};
struct DnsData
{
nsCOMPtr<nsIDNSService> serv;
nsTArray<DNSCacheEntries> data;
nsCOMPtr<NetDashboardCallback> cb;
nsIThread* thread;
};
bool mEnableLogging;
struct SocketData mSock;
struct HttpData mHttp;
struct WebSocketData mWs;
struct DnsData mDns;
};
} } // namespace mozilla::net
#endif // nsDashboard_h__

View File

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDashboardTypes__
#define nsDashboardTypes__
namespace mozilla {
namespace net {
struct SocketInfo
{
nsCString host;
uint64_t sent;
uint64_t received;
uint16_t port;
bool active;
bool tcp;
};
struct DNSCacheEntries
{
nsCString hostname;
nsTArray<nsCString> hostaddr;
int8_t family;
int64_t expiration;
};
struct HttpConnInfo
{
uint32_t ttl;
uint32_t rtt;
};
struct HttpRetParams
{
nsCString host;
nsTArray<HttpConnInfo> active;
nsTArray<HttpConnInfo> idle;
uint32_t counter;
uint16_t port;
bool spdy;
bool ssl;
};
} }
#endif // nsDashboardTypes__

View File

@ -21,6 +21,13 @@ EXPORTS = \
nsURLHelper.h \
$(NULL)
EXPORTS_NAMESPACES = mozilla/net
EXPORTS_mozilla/net = \
Dashboard.h \
DashboardTypes.h \
$(NULL)
CPPSRCS = \
nsTransportUtils.cpp \
nsAsyncStreamCopier.cpp \
@ -67,6 +74,7 @@ CPPSRCS = \
nsPreloadedStream.cpp \
nsStreamListenerWrapper.cpp \
ProxyAutoConfig.cpp \
Dashboard.cpp \
NetworkActivityMonitor.cpp \
$(NULL)

View File

@ -24,6 +24,8 @@ public:
virtual void OnSocketDetached(PRFileDesc *fd);
virtual void IsLocal(bool *aIsLocal);
virtual uint64_t ByteCountSent() { return 0; }
virtual uint64_t ByteCountReceived() { return 0; }
nsServerSocket();
// This must be public to support older compilers (xlC_r on AIX)

View File

@ -131,6 +131,8 @@ public:
// called when a socket event is handled
void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param);
uint64_t ByteCountReceived() { return mInput.ByteCount(); }
uint64_t ByteCountSent() { return mOutput.ByteCount(); }
protected:
virtual ~nsSocketTransport();

View File

@ -34,6 +34,7 @@ void StopSSLServerCertVerificationThreads();
} } // namespace mozilla::psm
using namespace mozilla;
using namespace mozilla::net;
#if defined(PR_LOGGING)
PRLogModuleInfo *gSocketTransportLog = nullptr;
@ -1014,3 +1015,41 @@ nsSocketTransportService::DiscoverMaxCount()
return PR_SUCCESS;
}
void
nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data,
struct SocketContext *context, bool aActive)
{
PRFileDesc *aFD = context->mFD;
bool tcp = (PR_GetDescType(aFD) == PR_DESC_SOCKET_TCP);
PRNetAddr peer_addr;
PR_GetPeerName(aFD, &peer_addr);
char host[64] = {0};
PR_NetAddrToString(&peer_addr, host, sizeof(host));
uint16_t port;
if (peer_addr.raw.family == PR_AF_INET)
port = peer_addr.inet.port;
else
port = peer_addr.ipv6.port;
port = PR_ntohs(port);
uint64_t sent = context->mHandler->ByteCountSent();
uint64_t received = context->mHandler->ByteCountReceived();
SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp };
data->AppendElement(info);
}
void
nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data)
{
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
for (uint32_t i = 0; i < mActiveCount; i++)
AnalyzeConnection(data, &mActiveList[i], true);
for (uint32_t i = 0; i < mIdleCount; i++)
AnalyzeConnection(data, &mIdleList[i], false);
}

View File

@ -19,6 +19,7 @@
#include "nsASocketHandler.h"
#include "nsIObserver.h"
#include "mozilla/Mutex.h"
#include "mozilla/net/DashboardTypes.h"
//-----------------------------------------------------------------------------
@ -73,6 +74,9 @@ public:
return mActiveCount + mIdleCount < gMaxCount;
}
// Called by the networking dashboard
// Fills the passed array with socket information
void GetSocketConnections(nsTArray<mozilla::net::SocketInfo> *);
protected:
virtual ~nsSocketTransportService();
@ -183,6 +187,9 @@ private:
void ProbeMaxCount();
#endif
bool mProbedMaxCount;
void AnalyzeConnection(nsTArray<mozilla::net::SocketInfo> *data,
SocketContext *context, bool aActive);
};
extern nsSocketTransportService *gSocketTransportService;

View File

@ -867,6 +867,19 @@
NS_NETWORK_SOCKET_CONTRACTID_PREFIX "starttls"
#define NS_DASHBOARD_CLASSNAME \
"Dashboard"
#define NS_DASHBOARD_CONTRACTID \
"@mozilla.org/network/dashboard;1"
#define NS_DASHBOARD_CID \
{ /*c79eb3c6-091a-45a6-8544-5a8d1ab79537 */ \
0xc79eb3c6, \
0x091a, \
0x45a6, \
{ 0x85, 0x44, 0x5a, 0x8d, 0x1a, 0xb7, 0x95, 0x37 } \
}
/******************************************************************************
* netwerk/cookie classes
*/

View File

@ -232,6 +232,13 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
#endif // !NECKO_PROTOCOL_http
#include "mozilla/net/Dashboard.h"
namespace mozilla {
namespace net {
NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
}
}
#ifdef NECKO_PROTOCOL_res
// resource
#include "nsResProtocolHandler.h"
@ -694,6 +701,7 @@ NS_DEFINE_NAMED_CID(NS_BUFFEREDOUTPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_MIMEINPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_PROTOCOLPROXYSERVICE_CID);
NS_DEFINE_NAMED_CID(NS_STREAMCONVERTERSERVICE_CID);
NS_DEFINE_NAMED_CID(NS_DASHBOARD_CID);
#ifdef BUILD_APPLEFILE_DECODER
NS_DEFINE_NAMED_CID(NS_APPLEFILEDECODER_CID);
#endif
@ -826,6 +834,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
{ &kNS_MIMEINPUTSTREAM_CID, false, NULL, nsMIMEInputStreamConstructor },
{ &kNS_PROTOCOLPROXYSERVICE_CID, true, NULL, nsProtocolProxyServiceConstructor },
{ &kNS_STREAMCONVERTERSERVICE_CID, false, NULL, CreateNewStreamConvServiceFactory },
{ &kNS_DASHBOARD_CID, false, NULL, mozilla::net::DashboardConstructor },
#ifdef BUILD_APPLEFILE_DECODER
{ &kNS_APPLEFILEDECODER_CID, false, NULL, nsAppleFileDecoderConstructor },
#endif
@ -962,6 +971,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
{ NS_MIMEINPUTSTREAM_CONTRACTID, &kNS_MIMEINPUTSTREAM_CID },
{ NS_PROTOCOLPROXYSERVICE_CONTRACTID, &kNS_PROTOCOLPROXYSERVICE_CID },
{ NS_STREAMCONVERTERSERVICE_CONTRACTID, &kNS_STREAMCONVERTERSERVICE_CID },
{ NS_DASHBOARD_CONTRACTID, &kNS_DASHBOARD_CID },
#ifdef BUILD_APPLEFILE_DECODER
{ NS_IAPPLEFILEDECODER_CONTRACTID, &kNS_APPLEFILEDECODER_CID },
#endif

View File

@ -853,3 +853,11 @@ nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
return af;
}
NS_IMETHODIMP
nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
{
NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
mResolver->GetDNSCacheEntries(args);
return NS_OK;
}

View File

@ -38,6 +38,7 @@
#include "mozilla/Telemetry.h"
using namespace mozilla;
using namespace mozilla::net;
//----------------------------------------------------------------------------
@ -1026,3 +1027,55 @@ nsHostResolver::Create(uint32_t maxCacheEntries,
*result = res;
return rv;
}
PLDHashOperator
CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry,
uint32_t number, void *arg)
{
nsHostDBEnt *ent = static_cast<nsHostDBEnt *> (entry);
nsTArray<DNSCacheEntries> *args =
static_cast<nsTArray<DNSCacheEntries> *> (arg);
nsHostRecord *rec = ent->rec;
// Without addr_info, there is no meaning of adding this entry
if (rec->addr_info) {
DNSCacheEntries info;
const char *hostname;
PRNetAddr addr;
if (rec->host)
hostname = rec->host;
else // No need to add this entry if no host name is there
return PL_DHASH_NEXT;
uint32_t now = NowInMinutes();
info.expiration = ((int64_t) rec->expiration - now) * 60;
// We only need valid DNS cache entries
if (info.expiration <= 0)
return PL_DHASH_NEXT;
info.family = rec->af;
info.hostname = hostname;
{
MutexAutoLock lock(rec->addr_info_lock);
void *ptr = PR_EnumerateAddrInfo(nullptr, rec->addr_info, 0, &addr);
while (ptr) {
char buf[64];
if (PR_NetAddrToString(&addr, buf, sizeof(buf)) == PR_SUCCESS)
info.hostaddr.AppendElement(buf);
ptr = PR_EnumerateAddrInfo(ptr, rec->addr_info, 0, &addr);
}
}
args->AppendElement(info);
}
return PL_DHASH_NEXT;
}
void
nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries> *args)
{
PL_DHashTableEnumerate(&mDB, CacheEntryEnumerator, args);
}

View File

@ -17,6 +17,7 @@
#include "nsIDNSListener.h"
#include "nsString.h"
#include "nsTArray.h"
#include "mozilla/net/DashboardTypes.h"
class nsHostResolver;
class nsHostRecord;
@ -275,6 +276,12 @@ private:
bool mShutdown;
PRIntervalTime mLongIdleTimeout;
PRIntervalTime mShortIdleTimeout;
public:
/*
* Called by the networking dashboard via the DnsService2
*/
void GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *);
};
#endif // nsHostResolver_h__

View File

@ -9,10 +9,20 @@ interface nsIEventTarget;
interface nsIDNSRecord;
interface nsIDNSListener;
%{C++
#include "nsTArray.h"
namespace mozilla { namespace net {
struct DNSCacheEntries;
} }
%}
[ptr] native EntriesArray(nsTArray<mozilla::net::DNSCacheEntries>);
/**
* nsIDNSService
*/
[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)]
[scriptable, uuid(f1971942-19db-44bf-81e8-d15df220a39f)]
interface nsIDNSService : nsISupports
{
/**
@ -73,6 +83,13 @@ interface nsIDNSService : nsISupports
nsIDNSRecord resolve(in AUTF8String aHostName,
in unsigned long aFlags);
/**
* The method takes a pointer to an nsTArray
* and fills it with cache entry data
* Called by the networking dashboard
*/
[noscript] void getDNSCacheEntries(in EntriesArray args);
/**
* @return the hostname of the operating system.
*/

View File

@ -0,0 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http:mozilla.org/MPL/2.0/. */
#include "nsHttpHandler.h"
#include "HttpInfo.h"
void
mozilla::net::HttpInfo::
GetHttpConnectionData(nsTArray<HttpRetParams>* args)
{
if (gHttpHandler)
gHttpHandler->ConnMgr()->GetConnectionData(args);
}

View File

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http:mozilla.org/MPL/2.0/. */
#include "mozilla/net/DashboardTypes.h"
#ifndef nsHttpInfo__
#define nsHttpInfo__
namespace mozilla {
namespace net {
class HttpInfo
{
public:
/* Calls getConnectionData method in nsHttpConnectionMgr. */
static void GetHttpConnectionData(nsTArray<HttpRetParams> *);
};
} } // namespace mozilla::net
#endif // nsHttpInfo__

View File

@ -45,6 +45,7 @@ EXPORTS_mozilla/net += \
HttpChannelParent.h \
HttpChannelChild.h \
PHttpChannelParams.h \
HttpInfo.h \
$(NULL)
EXPORTS = \
@ -78,6 +79,7 @@ CPPSRCS = \
HttpChannelParent.cpp \
HttpChannelChild.cpp \
HttpChannelParentListener.cpp \
HttpInfo.cpp \
NullHttpTransaction.cpp \
ASpdySession.cpp \
SpdySession2.cpp \

View File

@ -131,6 +131,7 @@ public:
bool UsingSpdy() { return !!mUsingSpdyVersion; }
bool EverUsedSpdy() { return mEverUsedSpdy; }
PRIntervalTime Rtt() { return mRtt; }
// true when connection SSL NPN phase is complete and we know
// authoritatively whether UsingSpdy() or not.

View File

@ -3187,6 +3187,40 @@ nsConnectionEntry::MaxPipelineDepth(nsAHttpTransaction::Classifier aClass)
return mGreenDepth;
}
PLDHashOperator
nsHttpConnectionMgr::ReadConnectionEntry(const nsACString &key,
nsAutoPtr<nsConnectionEntry> &ent,
void *aArg)
{
nsTArray<HttpRetParams> *args = static_cast<nsTArray<HttpRetParams> *> (aArg);
HttpRetParams data;
data.host = ent->mConnInfo->Host();
data.port = ent->mConnInfo->Port();
for (uint32_t i = 0; i < ent->mActiveConns.Length(); i++) {
HttpConnInfo info;
info.ttl = ent->mActiveConns[i]->TimeToLive();
info.rtt = ent->mActiveConns[i]->Rtt();
data.active.AppendElement(info);
}
for (uint32_t i = 0; i < ent->mIdleConns.Length(); i++) {
HttpConnInfo info;
info.ttl = ent->mIdleConns[i]->TimeToLive();
info.rtt = ent->mIdleConns[i]->Rtt();
data.active.AppendElement(info);
}
data.spdy = ent->mUsingSpdy;
data.ssl = ent->mConnInfo->UsingSSL();
args->AppendElement(data);
return PL_DHASH_NEXT;
}
bool
nsHttpConnectionMgr::GetConnectionData(nsTArray<mozilla::net::HttpRetParams> *aArg)
{
mCT.Enumerate(ReadConnectionEntry, aArg);
return true;
}
uint32_t
nsHttpConnectionMgr::nsConnectionEntry::UnconnectedHalfOpens()
{

View File

@ -19,6 +19,7 @@
#include "nsISocketTransportService.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Attributes.h"
#include "mozilla/net/DashboardTypes.h"
#include "nsIObserver.h"
#include "nsITimer.h"
@ -223,6 +224,7 @@ public:
bool SupportsPipelining(nsHttpConnectionInfo *);
bool GetConnectionData(nsTArray<mozilla::net::HttpRetParams> *);
private:
virtual ~nsHttpConnectionMgr();
@ -611,6 +613,11 @@ private:
nsTHashtable<nsCStringHashKey> mAlternateProtocolHash;
static PLDHashOperator TrimAlternateProtocolHash(nsCStringHashKey *entry,
void *closure);
static PLDHashOperator ReadConnectionEntry(const nsACString &key,
nsAutoPtr<nsConnectionEntry> &ent,
void *aArg);
// Read Timeout Tick handlers
void ActivateTimeoutTick();
void TimeoutTick();

View File

@ -912,6 +912,8 @@ private:
// WebSocketChannel
//-----------------------------------------------------------------------------
uint32_t WebSocketChannel::sSerialSeed = 0;
WebSocketChannel::WebSocketChannel() :
mPort(0),
mCloseTimeout(20000),
@ -949,7 +951,8 @@ WebSocketChannel::WebSocketChannel() :
mCurrentOutSent(0),
mCompressor(nullptr),
mDynamicOutputSize(0),
mDynamicOutput(nullptr)
mDynamicOutput(nullptr),
mConnectionLogService(nullptr)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
@ -959,6 +962,13 @@ WebSocketChannel::WebSocketChannel() :
sWebSocketAdmissions = new nsWSAdmissionManager();
mFramePtr = mBuffer = static_cast<uint8_t *>(moz_xmalloc(mBufferSize));
nsresult rv;
mConnectionLogService = do_GetService("@mozilla.org/network/dashboard;1",&rv);
if (NS_FAILED(rv))
LOG(("Failed to initiate dashboard service."));
mSerial = sSerialSeed++;
}
WebSocketChannel::~WebSocketChannel()
@ -1320,6 +1330,15 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count)
}
NS_DispatchToMainThread(new CallOnMessageAvailable(this, utf8Data, -1));
nsresult rv;
if (mConnectionLogService) {
nsAutoCString host;
rv = mURI->GetHostPort(host);
if (NS_SUCCEEDED(rv)) {
mConnectionLogService->NewMsgReceived(host, mSerial, count);
LOG(("Added new msg received for %s",host.get()));
}
}
}
} else if (opcode & kControlFrameMask) {
// control frames
@ -1401,6 +1420,16 @@ WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count)
nsCString binaryData((const char *)payload, payloadLength);
NS_DispatchToMainThread(new CallOnMessageAvailable(this, binaryData,
payloadLength));
// To add the header to 'Networking Dashboard' log
nsresult rv;
if (mConnectionLogService) {
nsAutoCString host;
rv = mURI->GetHostPort(host);
if (NS_SUCCEEDED(rv)) {
mConnectionLogService->NewMsgReceived(host, mSerial, count);
LOG(("Added new received msg for %s",host.get()));
}
}
}
} else if (opcode != kContinuation) {
/* unknown opcode */
@ -1814,6 +1843,14 @@ WebSocketChannel::CleanupConnection()
mTransport = nullptr;
}
nsresult rv;
if (mConnectionLogService) {
nsAutoCString host;
rv = mURI->GetHostPort(host);
if (NS_SUCCEEDED(rv))
mConnectionLogService->RemoveHost(host, mSerial);
}
DecrementSessionCount();
}
@ -2638,6 +2675,14 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
if (NS_FAILED(rv))
return rv;
if (mConnectionLogService) {
nsAutoCString host;
rv = mURI->GetHostPort(host);
if (NS_SUCCEEDED(rv)) {
mConnectionLogService->AddHost(host, mSerial, BaseWebSocketChannel::mEncrypted);
}
}
rv = ApplyForAdmission();
if (NS_FAILED(rv))
return rv;
@ -2734,6 +2779,16 @@ WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary,
return NS_ERROR_FILE_TOO_BIG;
}
nsresult rv;
if (mConnectionLogService) {
nsAutoCString host;
rv = mURI->GetHostPort(host);
if (NS_SUCCEEDED(rv)) {
mConnectionLogService->NewMsgSent(host, mSerial, aLength);
LOG(("Added new msg sent for %s",host.get()));
}
}
return mSocketThread->Dispatch(
aStream ? new OutboundEnqueuer(this, new OutboundMessage(aStream, aLength))
: new OutboundEnqueuer(this,

View File

@ -26,6 +26,7 @@
#include "nsIHttpChannelInternal.h"
#include "nsIRandomGenerator.h"
#include "BaseWebSocketChannel.h"
#include "nsIDashboardEventNotifier.h"
#include "nsCOMPtr.h"
#include "nsString.h"
@ -242,6 +243,10 @@ private:
nsWSCompression *mCompressor;
uint32_t mDynamicOutputSize;
uint8_t *mDynamicOutput;
nsCOMPtr<nsIDashboardEventNotifier> mConnectionLogService;
uint32_t mSerial;
static uint32_t sSerialSeed;
};
class WebSocketSSLChannel : public WebSocketChannel