michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http:mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/dom/NetDashboardBinding.h" michael@0: #include "mozilla/net/Dashboard.h" michael@0: #include "mozilla/net/HttpInfo.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsHttp.h" michael@0: #include "nsICancelable.h" michael@0: #include "nsIDNSService.h" michael@0: #include "nsIDNSRecord.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsISocketTransport.h" michael@0: #include "nsIThread.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "nsSocketTransportService2.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsURLHelper.h" michael@0: michael@0: using mozilla::AutoSafeJSContext; michael@0: using mozilla::dom::Sequence; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: class SocketData michael@0: : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: SocketData() michael@0: { michael@0: mTotalSent = 0; michael@0: mTotalRecv = 0; michael@0: mThread = nullptr; michael@0: } michael@0: michael@0: virtual ~SocketData() michael@0: { michael@0: } michael@0: michael@0: uint64_t mTotalSent; michael@0: uint64_t mTotalRecv; michael@0: nsTArray mData; michael@0: nsMainThreadPtrHandle mCallback; michael@0: nsIThread *mThread; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS0(SocketData) michael@0: michael@0: michael@0: class HttpData michael@0: : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: HttpData() michael@0: { michael@0: mThread = nullptr; michael@0: } michael@0: michael@0: virtual ~HttpData() michael@0: { michael@0: } michael@0: michael@0: nsTArray mData; michael@0: nsMainThreadPtrHandle mCallback; michael@0: nsIThread *mThread; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS0(HttpData) michael@0: michael@0: michael@0: class WebSocketRequest michael@0: : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: WebSocketRequest() michael@0: { michael@0: mThread = nullptr; michael@0: } michael@0: michael@0: virtual ~WebSocketRequest() michael@0: { michael@0: } michael@0: michael@0: nsMainThreadPtrHandle mCallback; michael@0: nsIThread *mThread; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS0(WebSocketRequest) michael@0: michael@0: michael@0: class DnsData michael@0: : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: DnsData() michael@0: { michael@0: mThread = nullptr; michael@0: } michael@0: michael@0: virtual ~DnsData() michael@0: { michael@0: } michael@0: michael@0: nsTArray mData; michael@0: nsMainThreadPtrHandle mCallback; michael@0: nsIThread *mThread; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS0(DnsData) michael@0: michael@0: michael@0: class ConnectionData michael@0: : public nsITransportEventSink michael@0: , public nsITimerCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSITRANSPORTEVENTSINK michael@0: NS_DECL_NSITIMERCALLBACK michael@0: michael@0: void StartTimer(uint32_t aTimeout); michael@0: void StopTimer(); michael@0: michael@0: ConnectionData(Dashboard *target) michael@0: { michael@0: mThread = nullptr; michael@0: mDashboard = target; michael@0: } michael@0: michael@0: virtual ~ConnectionData() michael@0: { michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr mSocket; michael@0: nsCOMPtr mStreamIn; michael@0: nsCOMPtr mTimer; michael@0: nsMainThreadPtrHandle mCallback; michael@0: nsIThread *mThread; michael@0: Dashboard *mDashboard; michael@0: michael@0: nsCString mHost; michael@0: uint32_t mPort; michael@0: const char *mProtocol; michael@0: uint32_t mTimeout; michael@0: michael@0: nsString mStatus; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(ConnectionData, nsITransportEventSink, nsITimerCallback) michael@0: michael@0: NS_IMETHODIMP michael@0: ConnectionData::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, michael@0: uint64_t aProgress, uint64_t aProgressMax) michael@0: { michael@0: if (aStatus == NS_NET_STATUS_CONNECTED_TO) { michael@0: StopTimer(); michael@0: } michael@0: michael@0: CopyASCIItoUTF16(Dashboard::GetErrorString(aStatus), mStatus); michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (mDashboard, &Dashboard::GetConnectionStatus, this); michael@0: mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ConnectionData::Notify(nsITimer *aTimer) michael@0: { michael@0: MOZ_ASSERT(aTimer == mTimer); michael@0: michael@0: if (mSocket) { michael@0: mSocket->Close(NS_ERROR_ABORT); michael@0: mSocket = nullptr; michael@0: mStreamIn = nullptr; michael@0: } michael@0: michael@0: mTimer = nullptr; michael@0: michael@0: mStatus.Assign(NS_LITERAL_STRING("NS_ERROR_NET_TIMEOUT")); michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (mDashboard, &Dashboard::GetConnectionStatus, this); michael@0: mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: ConnectionData::StartTimer(uint32_t aTimeout) michael@0: { michael@0: if (!mTimer) { michael@0: mTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: } michael@0: michael@0: mTimer->InitWithCallback(this, aTimeout * 1000, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: void michael@0: ConnectionData::StopTimer() michael@0: { michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: mTimer = nullptr; michael@0: } michael@0: } michael@0: michael@0: michael@0: class LookupHelper; michael@0: michael@0: class LookupArgument michael@0: : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: LookupArgument(nsIDNSRecord *aRecord, LookupHelper *aHelper) michael@0: { michael@0: mRecord = aRecord; michael@0: mHelper = aHelper; michael@0: } michael@0: michael@0: virtual ~LookupArgument() michael@0: { michael@0: } michael@0: michael@0: nsCOMPtr mRecord; michael@0: nsRefPtr mHelper; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS0(LookupArgument) michael@0: michael@0: michael@0: class LookupHelper michael@0: : public nsIDNSListener michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIDNSLISTENER michael@0: michael@0: LookupHelper() { michael@0: } michael@0: michael@0: virtual ~LookupHelper() michael@0: { michael@0: if (mCancel) { michael@0: mCancel->Cancel(NS_ERROR_ABORT); michael@0: } michael@0: } michael@0: michael@0: nsresult ConstructAnswer(LookupArgument *aArgument); michael@0: public: michael@0: nsCOMPtr mCancel; michael@0: nsMainThreadPtrHandle mCallback; michael@0: nsIThread *mThread; michael@0: nsresult mStatus; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(LookupHelper, nsIDNSListener) michael@0: michael@0: NS_IMETHODIMP michael@0: LookupHelper::OnLookupComplete(nsICancelable *aRequest, michael@0: nsIDNSRecord *aRecord, nsresult aStatus) michael@0: { michael@0: MOZ_ASSERT(aRequest == mCancel); michael@0: mCancel = nullptr; michael@0: mStatus = aStatus; michael@0: michael@0: nsRefPtr arg = new LookupArgument(aRecord, this); michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg >( michael@0: this, &LookupHelper::ConstructAnswer, arg); michael@0: mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: LookupHelper::ConstructAnswer(LookupArgument *aArgument) michael@0: { michael@0: michael@0: nsIDNSRecord *aRecord = aArgument->mRecord; michael@0: AutoSafeJSContext cx; michael@0: michael@0: mozilla::dom::DNSLookupDict dict; michael@0: dict.mAddress.Construct(); michael@0: michael@0: Sequence &addresses = dict.mAddress.Value(); michael@0: michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: dict.mAnswer = true; michael@0: bool hasMore; michael@0: aRecord->HasMore(&hasMore); michael@0: while (hasMore) { michael@0: nsCString nextAddress; michael@0: aRecord->GetNextAddrAsString(nextAddress); michael@0: CopyASCIItoUTF16(nextAddress, *addresses.AppendElement()); michael@0: aRecord->HasMore(&hasMore); michael@0: } michael@0: } else { michael@0: dict.mAnswer = false; michael@0: CopyASCIItoUTF16(Dashboard::GetErrorString(mStatus), dict.mError); michael@0: } michael@0: michael@0: JS::RootedValue val(cx); michael@0: if (!dict.ToObject(cx, &val)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: this->mCallback->OnDashboardDataAvailable(val); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(Dashboard, nsIDashboard, nsIDashboardEventNotifier) michael@0: michael@0: Dashboard::Dashboard() michael@0: { michael@0: mEnableLogging = false; michael@0: } michael@0: michael@0: Dashboard::~Dashboard() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RequestSockets(NetDashboardCallback *aCallback) michael@0: { michael@0: nsRefPtr socketData = new SocketData(); michael@0: socketData->mCallback = michael@0: new nsMainThreadPtrHolder(aCallback, true); michael@0: socketData->mThread = NS_GetCurrentThread(); michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetSocketsDispatch, socketData); michael@0: gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetSocketsDispatch(SocketData *aSocketData) michael@0: { michael@0: nsRefPtr socketData = aSocketData; michael@0: if (gSocketTransportService) { michael@0: gSocketTransportService->GetSocketConnections(&socketData->mData); michael@0: socketData->mTotalSent = gSocketTransportService->GetSentBytes(); michael@0: socketData->mTotalRecv = gSocketTransportService->GetReceivedBytes(); michael@0: } michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetSockets, socketData); michael@0: socketData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetSockets(SocketData *aSocketData) michael@0: { michael@0: nsRefPtr socketData = aSocketData; michael@0: AutoSafeJSContext cx; michael@0: michael@0: mozilla::dom::SocketsDict dict; michael@0: dict.mSockets.Construct(); michael@0: dict.mSent = 0; michael@0: dict.mReceived = 0; michael@0: michael@0: Sequence &sockets = dict.mSockets.Value(); michael@0: michael@0: uint32_t length = socketData->mData.Length(); michael@0: if (!sockets.SetCapacity(length)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < socketData->mData.Length(); i++) { michael@0: mozilla::dom::SocketElement &mSocket = *sockets.AppendElement(); michael@0: CopyASCIItoUTF16(socketData->mData[i].host, mSocket.mHost); michael@0: mSocket.mPort = socketData->mData[i].port; michael@0: mSocket.mActive = socketData->mData[i].active; michael@0: mSocket.mTcp = socketData->mData[i].tcp; michael@0: mSocket.mSent = (double) socketData->mData[i].sent; michael@0: mSocket.mReceived = (double) socketData->mData[i].received; michael@0: dict.mSent += socketData->mData[i].sent; michael@0: dict.mReceived += socketData->mData[i].received; michael@0: } michael@0: michael@0: dict.mSent += socketData->mTotalSent; michael@0: dict.mReceived += socketData->mTotalRecv; michael@0: JS::RootedValue val(cx); michael@0: if (!dict.ToObject(cx, &val)) michael@0: return NS_ERROR_FAILURE; michael@0: socketData->mCallback->OnDashboardDataAvailable(val); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RequestHttpConnections(NetDashboardCallback *aCallback) michael@0: { michael@0: nsRefPtr httpData = new HttpData(); michael@0: httpData->mCallback = michael@0: new nsMainThreadPtrHolder(aCallback, true); michael@0: httpData->mThread = NS_GetCurrentThread(); michael@0: michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetHttpDispatch, httpData); michael@0: gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetHttpDispatch(HttpData *aHttpData) michael@0: { michael@0: nsRefPtr httpData = aHttpData; michael@0: HttpInfo::GetHttpConnectionData(&httpData->mData); michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetHttpConnections, httpData); michael@0: httpData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: Dashboard::GetHttpConnections(HttpData *aHttpData) michael@0: { michael@0: nsRefPtr httpData = aHttpData; michael@0: AutoSafeJSContext cx; michael@0: michael@0: mozilla::dom::HttpConnDict dict; michael@0: dict.mConnections.Construct(); michael@0: michael@0: using mozilla::dom::HalfOpenInfoDict; michael@0: using mozilla::dom::HttpConnectionElement; michael@0: using mozilla::dom::HttpConnInfo; michael@0: Sequence &connections = dict.mConnections.Value(); michael@0: michael@0: uint32_t length = httpData->mData.Length(); michael@0: if (!connections.SetCapacity(length)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < httpData->mData.Length(); i++) { michael@0: HttpConnectionElement &connection = *connections.AppendElement(); michael@0: michael@0: CopyASCIItoUTF16(httpData->mData[i].host, connection.mHost); michael@0: connection.mPort = httpData->mData[i].port; michael@0: connection.mSpdy = httpData->mData[i].spdy; michael@0: connection.mSsl = httpData->mData[i].ssl; michael@0: michael@0: connection.mActive.Construct(); michael@0: connection.mIdle.Construct(); michael@0: connection.mHalfOpens.Construct(); michael@0: michael@0: Sequence &active = connection.mActive.Value(); michael@0: Sequence &idle = connection.mIdle.Value(); michael@0: Sequence &halfOpens = connection.mHalfOpens.Value(); michael@0: michael@0: if (!active.SetCapacity(httpData->mData[i].active.Length()) || michael@0: !idle.SetCapacity(httpData->mData[i].idle.Length()) || michael@0: !halfOpens.SetCapacity(httpData->mData[i].halfOpens.Length())) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: for (uint32_t j = 0; j < httpData->mData[i].active.Length(); j++) { michael@0: HttpConnInfo &info = *active.AppendElement(); michael@0: info.mRtt = httpData->mData[i].active[j].rtt; michael@0: info.mTtl = httpData->mData[i].active[j].ttl; michael@0: info.mProtocolVersion = michael@0: httpData->mData[i].active[j].protocolVersion; michael@0: } michael@0: michael@0: for (uint32_t j = 0; j < httpData->mData[i].idle.Length(); j++) { michael@0: HttpConnInfo &info = *idle.AppendElement(); michael@0: info.mRtt = httpData->mData[i].idle[j].rtt; michael@0: info.mTtl = httpData->mData[i].idle[j].ttl; michael@0: info.mProtocolVersion = httpData->mData[i].idle[j].protocolVersion; michael@0: } michael@0: michael@0: for (uint32_t j = 0; j < httpData->mData[i].halfOpens.Length(); j++) { michael@0: HalfOpenInfoDict &info = *halfOpens.AppendElement(); michael@0: info.mSpeculative = httpData->mData[i].halfOpens[j].speculative; michael@0: } michael@0: } michael@0: michael@0: JS::RootedValue val(cx); michael@0: if (!dict.ToObject(cx, &val)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: httpData->mCallback->OnDashboardDataAvailable(val); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::GetEnableLogging(bool *value) michael@0: { michael@0: *value = mEnableLogging; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::SetEnableLogging(const bool value) michael@0: { michael@0: mEnableLogging = value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::AddHost(const nsACString& aHost, uint32_t aSerial, bool aEncrypted) michael@0: { michael@0: if (mEnableLogging) { michael@0: mozilla::MutexAutoLock lock(mWs.lock); michael@0: LogData mData(nsCString(aHost), aSerial, aEncrypted); michael@0: if (mWs.data.Contains(mData)) { michael@0: return NS_OK; michael@0: } michael@0: if (!mWs.data.AppendElement(mData)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RemoveHost(const nsACString& aHost, uint32_t aSerial) michael@0: { michael@0: if (mEnableLogging) { michael@0: mozilla::MutexAutoLock lock(mWs.lock); michael@0: int32_t index = mWs.IndexOf(nsCString(aHost), aSerial); michael@0: if (index == -1) michael@0: return NS_ERROR_FAILURE; michael@0: mWs.data.RemoveElementAt(index); michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::NewMsgSent(const nsACString& aHost, uint32_t aSerial, uint32_t aLength) michael@0: { michael@0: if (mEnableLogging) { michael@0: mozilla::MutexAutoLock lock(mWs.lock); michael@0: int32_t index = mWs.IndexOf(nsCString(aHost), aSerial); michael@0: if (index == -1) michael@0: return NS_ERROR_FAILURE; michael@0: mWs.data[index].mMsgSent++; michael@0: mWs.data[index].mSizeSent += aLength; michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::NewMsgReceived(const nsACString& aHost, uint32_t aSerial, uint32_t aLength) michael@0: { michael@0: if (mEnableLogging) { michael@0: mozilla::MutexAutoLock lock(mWs.lock); michael@0: int32_t index = mWs.IndexOf(nsCString(aHost), aSerial); michael@0: if (index == -1) michael@0: return NS_ERROR_FAILURE; michael@0: mWs.data[index].mMsgReceived++; michael@0: mWs.data[index].mSizeReceived += aLength; michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RequestWebsocketConnections(NetDashboardCallback *aCallback) michael@0: { michael@0: nsRefPtr wsRequest = new WebSocketRequest(); michael@0: wsRequest->mCallback = michael@0: new nsMainThreadPtrHolder(aCallback, true); michael@0: wsRequest->mThread = NS_GetCurrentThread(); michael@0: michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetWebSocketConnections, wsRequest); michael@0: wsRequest->mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetWebSocketConnections(WebSocketRequest *aWsRequest) michael@0: { michael@0: nsRefPtr wsRequest = aWsRequest; michael@0: AutoSafeJSContext cx; michael@0: michael@0: mozilla::dom::WebSocketDict dict; michael@0: dict.mWebsockets.Construct(); michael@0: Sequence &websockets = michael@0: dict.mWebsockets.Value(); michael@0: michael@0: mozilla::MutexAutoLock lock(mWs.lock); michael@0: uint32_t length = mWs.data.Length(); michael@0: if (!websockets.SetCapacity(length)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mWs.data.Length(); i++) { michael@0: mozilla::dom::WebSocketElement &websocket = *websockets.AppendElement(); michael@0: CopyASCIItoUTF16(mWs.data[i].mHost, websocket.mHostport); michael@0: websocket.mMsgsent = mWs.data[i].mMsgSent; michael@0: websocket.mMsgreceived = mWs.data[i].mMsgReceived; michael@0: websocket.mSentsize = mWs.data[i].mSizeSent; michael@0: websocket.mReceivedsize = mWs.data[i].mSizeReceived; michael@0: websocket.mEncrypted = mWs.data[i].mEncrypted; michael@0: } michael@0: michael@0: JS::RootedValue val(cx); michael@0: if (!dict.ToObject(cx, &val)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: wsRequest->mCallback->OnDashboardDataAvailable(val); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RequestDNSInfo(NetDashboardCallback *aCallback) michael@0: { michael@0: nsRefPtr dnsData = new DnsData(); michael@0: dnsData->mCallback = michael@0: new nsMainThreadPtrHolder(aCallback, true); michael@0: michael@0: nsresult rv; michael@0: dnsData->mData.Clear(); michael@0: dnsData->mThread = NS_GetCurrentThread(); michael@0: michael@0: if (!mDnsService) { michael@0: mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetDnsInfoDispatch, dnsData); michael@0: gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetDnsInfoDispatch(DnsData *aDnsData) michael@0: { michael@0: nsRefPtr dnsData = aDnsData; michael@0: if (mDnsService) { michael@0: mDnsService->GetDNSCacheEntries(&dnsData->mData); michael@0: } michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetDNSCacheEntries, dnsData); michael@0: dnsData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetDNSCacheEntries(DnsData *dnsData) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: michael@0: mozilla::dom::DNSCacheDict dict; michael@0: dict.mEntries.Construct(); michael@0: Sequence &entries = dict.mEntries.Value(); michael@0: michael@0: uint32_t length = dnsData->mData.Length(); michael@0: if (!entries.SetCapacity(length)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < dnsData->mData.Length(); i++) { michael@0: mozilla::dom::DnsCacheEntry &entry = *entries.AppendElement(); michael@0: entry.mHostaddr.Construct(); michael@0: michael@0: Sequence &addrs = entry.mHostaddr.Value(); michael@0: if (!addrs.SetCapacity(dnsData->mData[i].hostaddr.Length())) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: CopyASCIItoUTF16(dnsData->mData[i].hostname, entry.mHostname); michael@0: entry.mExpiration = dnsData->mData[i].expiration; michael@0: michael@0: for (uint32_t j = 0; j < dnsData->mData[i].hostaddr.Length(); j++) { michael@0: CopyASCIItoUTF16(dnsData->mData[i].hostaddr[j], michael@0: *addrs.AppendElement()); michael@0: } michael@0: michael@0: if (dnsData->mData[i].family == PR_AF_INET6) { michael@0: CopyASCIItoUTF16("ipv6", entry.mFamily); michael@0: } else { michael@0: CopyASCIItoUTF16("ipv4", entry.mFamily); michael@0: } michael@0: } michael@0: michael@0: JS::RootedValue val(cx); michael@0: if (!dict.ToObject(cx, &val)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: dnsData->mCallback->OnDashboardDataAvailable(val); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RequestDNSLookup(const nsACString &aHost, michael@0: NetDashboardCallback *aCallback) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!mDnsService) { michael@0: mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: nsRefPtr helper = new LookupHelper(); michael@0: helper->mCallback = michael@0: new nsMainThreadPtrHolder(aCallback, true); michael@0: helper->mThread = NS_GetCurrentThread(); michael@0: rv = mDnsService->AsyncResolve(aHost, 0, helper.get(), michael@0: NS_GetCurrentThread(), michael@0: getter_AddRefs(helper->mCancel)); michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: HttpConnInfo::SetHTTP1ProtocolVersion(uint8_t pv) michael@0: { michael@0: switch (pv) { michael@0: case NS_HTTP_VERSION_0_9: michael@0: protocolVersion.Assign(NS_LITERAL_STRING("http/0.9")); michael@0: break; michael@0: case NS_HTTP_VERSION_1_0: michael@0: protocolVersion.Assign(NS_LITERAL_STRING("http/1.0")); michael@0: break; michael@0: case NS_HTTP_VERSION_1_1: michael@0: protocolVersion.Assign(NS_LITERAL_STRING("http/1.1")); michael@0: break; michael@0: case NS_HTTP_VERSION_2_0: michael@0: protocolVersion.Assign(NS_LITERAL_STRING("http/2.0")); michael@0: break; michael@0: default: michael@0: protocolVersion.Assign(NS_LITERAL_STRING("unknown protocol version")); michael@0: } michael@0: } michael@0: michael@0: void michael@0: HttpConnInfo::SetHTTP2ProtocolVersion(uint8_t pv) michael@0: { michael@0: if (pv == SPDY_VERSION_3) { michael@0: protocolVersion.Assign(NS_LITERAL_STRING("spdy/3")); michael@0: } else if (pv == SPDY_VERSION_31) { michael@0: protocolVersion.Assign(NS_LITERAL_STRING("spdy/3.1")); michael@0: } else { michael@0: MOZ_ASSERT (pv == NS_HTTP2_DRAFT_VERSION); michael@0: protocolVersion.Assign(NS_LITERAL_STRING(NS_HTTP2_DRAFT_TOKEN)); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Dashboard::RequestConnection(const nsACString& aHost, uint32_t aPort, michael@0: const char *aProtocol, uint32_t aTimeout, michael@0: NetDashboardCallback *aCallback) michael@0: { michael@0: nsresult rv; michael@0: nsRefPtr connectionData = new ConnectionData(this); michael@0: connectionData->mHost = aHost; michael@0: connectionData->mPort = aPort; michael@0: connectionData->mProtocol = aProtocol; michael@0: connectionData->mTimeout = aTimeout; michael@0: michael@0: connectionData->mCallback = michael@0: new nsMainThreadPtrHolder(aCallback, true); michael@0: connectionData->mThread = NS_GetCurrentThread(); michael@0: michael@0: rv = TestNewConnection(connectionData); michael@0: if (NS_FAILED(rv)) { michael@0: CopyASCIItoUTF16(GetErrorString(rv), connectionData->mStatus); michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethodWithArg > michael@0: (this, &Dashboard::GetConnectionStatus, connectionData); michael@0: connectionData->mThread->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::GetConnectionStatus(ConnectionData *aConnectionData) michael@0: { michael@0: nsRefPtr connectionData = aConnectionData; michael@0: AutoSafeJSContext cx; michael@0: michael@0: mozilla::dom::ConnStatusDict dict; michael@0: dict.mStatus = connectionData->mStatus; michael@0: michael@0: JS::RootedValue val(cx); michael@0: if (!dict.ToObject(cx, &val)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: connectionData->mCallback->OnDashboardDataAvailable(val); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Dashboard::TestNewConnection(ConnectionData *aConnectionData) michael@0: { michael@0: nsRefPtr connectionData = aConnectionData; michael@0: michael@0: nsresult rv; michael@0: if (!connectionData->mHost.Length() || michael@0: !net_IsValidHostName(connectionData->mHost)) { michael@0: return NS_ERROR_UNKNOWN_HOST; michael@0: } michael@0: michael@0: if (connectionData->mProtocol && michael@0: NS_LITERAL_STRING("ssl").EqualsASCII(connectionData->mProtocol)) { michael@0: rv = gSocketTransportService->CreateTransport( michael@0: &connectionData->mProtocol, 1, connectionData->mHost, michael@0: connectionData->mPort, nullptr, michael@0: getter_AddRefs(connectionData->mSocket)); michael@0: } else { michael@0: rv = gSocketTransportService->CreateTransport( michael@0: nullptr, 0, connectionData->mHost, michael@0: connectionData->mPort, nullptr, michael@0: getter_AddRefs(connectionData->mSocket)); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = connectionData->mSocket->SetEventSink(connectionData, michael@0: NS_GetCurrentThread()); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = connectionData->mSocket->OpenInputStream( michael@0: nsITransport::OPEN_BLOCKING, 0, 0, michael@0: getter_AddRefs(connectionData->mStreamIn)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: connectionData->StartTimer(connectionData->mTimeout); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: typedef struct michael@0: { michael@0: nsresult key; michael@0: const char *error; michael@0: } ErrorEntry; michael@0: michael@0: #undef ERROR michael@0: #define ERROR(key, val) {key, #key} michael@0: michael@0: ErrorEntry errors[] = { michael@0: #include "ErrorList.h" michael@0: }; michael@0: michael@0: ErrorEntry socketTransportStatuses[] = { michael@0: ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)), michael@0: ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)), michael@0: ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)), michael@0: ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)), michael@0: ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)), michael@0: ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)), michael@0: ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)), michael@0: }; michael@0: #undef ERROR michael@0: michael@0: const char * michael@0: Dashboard::GetErrorString(nsresult rv) michael@0: { michael@0: int length = sizeof(socketTransportStatuses) / sizeof(ErrorEntry); michael@0: for (int i = 0;i < length;i++) michael@0: if (socketTransportStatuses[i].key == rv) { michael@0: return socketTransportStatuses[i].error; michael@0: } michael@0: michael@0: length = sizeof(errors) / sizeof(ErrorEntry); michael@0: for (int i = 0;i < length;i++) michael@0: if (errors[i].key == rv) { michael@0: return errors[i].error; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: } } // namespace mozilla::net