mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 03:05:34 +00:00
514 lines
13 KiB
C++
514 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* 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 "NetworkActivityMonitor.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsPISocketTransportService.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsSocketTransport2.h"
|
|
#include "nsSocketTransportService2.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/Services.h"
|
|
#include "prerror.h"
|
|
#include "prio.h"
|
|
#include "prmem.h"
|
|
#include <vector>
|
|
|
|
|
|
using namespace mozilla::net;
|
|
|
|
|
|
struct SocketActivity {
|
|
PROsfd fd;
|
|
uint32_t port;
|
|
nsString host;
|
|
uint32_t rx;
|
|
uint32_t tx;
|
|
};
|
|
|
|
mozilla::StaticRefPtr<NetworkActivityMonitor> gInstance;
|
|
static PRDescIdentity sNetActivityMonitorLayerIdentity;
|
|
static PRIOMethods sNetActivityMonitorLayerMethods;
|
|
static PRIOMethods *sNetActivityMonitorLayerMethodsPtr = nullptr;
|
|
|
|
static PRStatus
|
|
nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PRStatus ret;
|
|
PRErrorCode code;
|
|
ret = fd->lower->methods->connect(fd->lower, addr, timeout);
|
|
if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR ||
|
|
code == PR_IN_PROGRESS_ERROR) {
|
|
NetworkActivityMonitor::RegisterFd(fd, addr);
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, 0);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static PRStatus
|
|
nsNetMon_Close(PRFileDesc *fd)
|
|
{
|
|
if (!fd) {
|
|
return PR_FAILURE;
|
|
}
|
|
NetworkActivityMonitor::UnregisterFd(fd);
|
|
PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
|
|
MOZ_RELEASE_ASSERT(layer &&
|
|
layer->identity == sNetActivityMonitorLayerIdentity,
|
|
"NetActivityMonitor Layer not on top of stack");
|
|
layer->dtor(layer);
|
|
return fd->methods->close(fd);
|
|
}
|
|
|
|
|
|
static int32_t
|
|
nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->read(fd->lower, buf, len);
|
|
if (ret >= 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, fd, len);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->write(fd->lower, buf, len);
|
|
if (ret > 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, len);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_Writev(PRFileDesc *fd,
|
|
const PRIOVec *iov,
|
|
int32_t size,
|
|
PRIntervalTime timeout)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->writev(fd->lower, iov, size, timeout);
|
|
if (ret > 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, size);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_Recv(PRFileDesc *fd,
|
|
void *buf,
|
|
int32_t amount,
|
|
int flags,
|
|
PRIntervalTime timeout)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout);
|
|
if (ret >= 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, fd, amount);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_Send(PRFileDesc *fd,
|
|
const void *buf,
|
|
int32_t amount,
|
|
int flags,
|
|
PRIntervalTime timeout)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
|
|
if (ret > 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, amount);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_RecvFrom(PRFileDesc *fd,
|
|
void *buf,
|
|
int32_t amount,
|
|
int flags,
|
|
PRNetAddr *addr,
|
|
PRIntervalTime timeout)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->recvfrom(fd->lower,
|
|
buf,
|
|
amount,
|
|
flags,
|
|
addr,
|
|
timeout);
|
|
if (ret >= 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, fd, amount);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_SendTo(PRFileDesc *fd,
|
|
const void *buf,
|
|
int32_t amount,
|
|
int flags,
|
|
const PRNetAddr *addr,
|
|
PRIntervalTime timeout)
|
|
{
|
|
int32_t ret;
|
|
ret = fd->lower->methods->sendto(fd->lower,
|
|
buf,
|
|
amount,
|
|
flags,
|
|
addr,
|
|
timeout);
|
|
if (ret > 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, amount);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int32_t
|
|
nsNetMon_AcceptRead(PRFileDesc *listenSock,
|
|
PRFileDesc **acceptedSock,
|
|
PRNetAddr **peerAddr,
|
|
void *buf,
|
|
int32_t amount,
|
|
PRIntervalTime timeout)
|
|
{
|
|
int32_t ret;
|
|
ret = listenSock->lower->methods->acceptread(listenSock->lower,
|
|
acceptedSock,
|
|
peerAddr,
|
|
buf,
|
|
amount,
|
|
timeout);
|
|
if (ret > 0) {
|
|
NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, listenSock, amount);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(NetworkData, nsINetworkActivityData);
|
|
|
|
NS_IMETHODIMP
|
|
NetworkData::GetHost(nsAString& aHost) {
|
|
aHost = mHost;
|
|
return NS_OK;
|
|
};
|
|
|
|
NS_IMETHODIMP
|
|
NetworkData::GetPort(int32_t* aPort) {
|
|
*aPort = mPort;
|
|
return NS_OK;
|
|
};
|
|
|
|
NS_IMETHODIMP
|
|
NetworkData::GetRx(int32_t* aRx) {
|
|
*aRx = mRx;
|
|
return NS_OK;
|
|
};
|
|
|
|
NS_IMETHODIMP
|
|
NetworkData::GetTx(int32_t* aTx) {
|
|
*aTx = mTx;
|
|
return NS_OK;
|
|
};
|
|
|
|
NS_IMETHODIMP
|
|
NetworkData::GetFd(int32_t* aFd) {
|
|
*aFd = mFd;
|
|
return NS_OK;
|
|
};
|
|
|
|
class NotifyNetworkActivity : public mozilla::Runnable {
|
|
public:
|
|
explicit NotifyNetworkActivity(NetworkActivity* aActivity)
|
|
: mozilla::Runnable("NotifyNetworkActivity")
|
|
|
|
{
|
|
uint32_t rx;
|
|
uint32_t tx;
|
|
PROsfd fd;
|
|
for (auto iter = aActivity->rx.Iter(); !iter.Done(); iter.Next()) {
|
|
rx = iter.Data();
|
|
fd = iter.Key();
|
|
tx = aActivity->tx.Get(fd);
|
|
if (rx == 0 && tx == 0) {
|
|
// nothing to do
|
|
} else {
|
|
SocketActivity activity;
|
|
activity.fd = fd;
|
|
activity.rx = rx;
|
|
activity.tx = tx;
|
|
activity.host = aActivity->host.Get(fd);
|
|
activity.port = aActivity->port.Get(fd);
|
|
mActivities.AppendElement(activity);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
Run() override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mActivities.Length() == 0) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (!obs) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
|
if (NS_WARN_IF(!array)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
for (unsigned long i = 0; i < mActivities.Length(); i++) {
|
|
nsCOMPtr<nsINetworkActivityData> data =
|
|
new NetworkData(mActivities[i].host,
|
|
mActivities[i].port,
|
|
mActivities[i].fd,
|
|
mActivities[i].rx,
|
|
mActivities[i].tx);
|
|
|
|
nsresult rv = array->AppendElement(data);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
obs->NotifyObservers(array, NS_NETWORK_ACTIVITY, nullptr);
|
|
return NS_OK;
|
|
}
|
|
private:
|
|
nsTArray<SocketActivity> mActivities;
|
|
};
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(NetworkActivityMonitor, nsITimerCallback, nsINamed)
|
|
|
|
NetworkActivityMonitor::NetworkActivityMonitor()
|
|
: mInterval(PR_INTERVAL_NO_TIMEOUT)
|
|
, mLock("NetworkActivityMonitor::mLock")
|
|
{
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
MOZ_ASSERT(!mon, "multiple NetworkActivityMonitor instances!");
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
NetworkActivityMonitor::Notify(nsITimer* aTimer)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
nsCOMPtr<nsIRunnable> ev = new NotifyNetworkActivity(&mActivity);
|
|
NS_DispatchToMainThread(ev);
|
|
// reset the counters
|
|
for (auto iter = mActivity.host.Iter(); !iter.Done(); iter.Next()) {
|
|
uint32_t fd = iter.Key();
|
|
mActivity.tx.Put(fd, 0);
|
|
mActivity.rx.Put(fd, 0);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
NetworkActivityMonitor::GetName(nsACString& aName)
|
|
{
|
|
aName.AssignLiteral("NetworkActivityMonitor");
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::Init(int32_t aInterval)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
if (mon) {
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
|
}
|
|
mon = new NetworkActivityMonitor();
|
|
rv = mon->Init_Internal(aInterval);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
gInstance = mon;
|
|
} else {
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::Shutdown()
|
|
{
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
if (!mon) {
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
mon->Shutdown_Internal();
|
|
gInstance = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::Init_Internal(int32_t aInterval)
|
|
{
|
|
if (!sNetActivityMonitorLayerMethodsPtr) {
|
|
sNetActivityMonitorLayerIdentity =
|
|
PR_GetUniqueIdentity("network activity monitor layer");
|
|
sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods();
|
|
sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect;
|
|
sNetActivityMonitorLayerMethods.read = nsNetMon_Read;
|
|
sNetActivityMonitorLayerMethods.write = nsNetMon_Write;
|
|
sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev;
|
|
sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv;
|
|
sNetActivityMonitorLayerMethods.send = nsNetMon_Send;
|
|
sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom;
|
|
sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo;
|
|
sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead;
|
|
sNetActivityMonitorLayerMethods.close = nsNetMon_Close;
|
|
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
|
|
}
|
|
|
|
// create and fire the timer
|
|
mInterval = aInterval;
|
|
mTimer = NS_NewTimer();
|
|
if (!mTimer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return mTimer->InitWithCallback(this, mInterval, nsITimer::TYPE_REPEATING_SLACK);
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::Shutdown_Internal()
|
|
{
|
|
mTimer->Cancel();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd)
|
|
{
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
if (!mon) {
|
|
return NS_OK;
|
|
}
|
|
PRFileDesc* layer;
|
|
PRStatus status;
|
|
layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
|
|
sNetActivityMonitorLayerMethodsPtr);
|
|
if (!layer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer);
|
|
|
|
if (status == PR_FAILURE) {
|
|
PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::RegisterFd(PRFileDesc *aFd, const PRNetAddr *aAddr) {
|
|
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
if (!mon) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
PROsfd osfd = PR_FileDesc2NativeHandle(aFd);
|
|
if (NS_WARN_IF(osfd == -1)) {
|
|
return ErrorAccordingToNSPR(PR_GetError());
|
|
}
|
|
uint16_t port;
|
|
if (aAddr->raw.family == PR_AF_INET) {
|
|
port = aAddr->inet.port;
|
|
} else {
|
|
port = aAddr->ipv6.port;
|
|
}
|
|
char _host[net::kNetAddrMaxCStrBufSize] = {0};
|
|
nsAutoCString host;
|
|
if (PR_NetAddrToString(aAddr, _host, sizeof(_host) - 1) == PR_SUCCESS) {
|
|
host.Assign(_host);
|
|
} else {
|
|
host.AppendPrintf("N/A");
|
|
}
|
|
return mon->RegisterFd_Internal(osfd, NS_ConvertUTF8toUTF16(host), port);
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::UnregisterFd(PRFileDesc *aFd)
|
|
{
|
|
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
if (!mon) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
PROsfd osfd = PR_FileDesc2NativeHandle(aFd);
|
|
if (NS_WARN_IF(osfd == -1)) {
|
|
return ErrorAccordingToNSPR(PR_GetError());
|
|
}
|
|
return mon->UnregisterFd_Internal(osfd);
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::UnregisterFd_Internal(PROsfd aOsfd)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
// XXX indefinitely growing list
|
|
mActivity.active.Put(aOsfd, false);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::RegisterFd_Internal(PROsfd aOsfd, const nsString& host, uint16_t port)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
mActivity.port.Put(aOsfd, port);
|
|
mActivity.host.Put(aOsfd, host);
|
|
mActivity.rx.Put(aOsfd, 0);
|
|
mActivity.tx.Put(aOsfd, 0);
|
|
mActivity.active.Put(aOsfd, true);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::DataInOut(Direction aDirection, PRFileDesc *aFd, uint32_t aAmount)
|
|
{
|
|
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
|
RefPtr<NetworkActivityMonitor> mon(gInstance);
|
|
if (!mon) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
PROsfd osfd = PR_FileDesc2NativeHandle(aFd);
|
|
if (NS_WARN_IF(osfd == -1)) {
|
|
return ErrorAccordingToNSPR(PR_GetError());
|
|
}
|
|
return mon->DataInOut_Internal(osfd, aDirection, aAmount);
|
|
}
|
|
|
|
nsresult
|
|
NetworkActivityMonitor::DataInOut_Internal(PROsfd aOsfd, Direction aDirection, uint32_t aAmount)
|
|
{
|
|
mozilla::MutexAutoLock lock(mLock);
|
|
uint32_t current;
|
|
if (aDirection == NetworkActivityMonitor::kUpload) {
|
|
current = mActivity.tx.Get(aOsfd);
|
|
mActivity.tx.Put(aOsfd, aAmount + current);
|
|
}
|
|
else {
|
|
current = mActivity.rx.Get(aOsfd);
|
|
mActivity.rx.Put(aOsfd, aAmount + current);
|
|
}
|
|
return NS_OK;
|
|
}
|