in netwerk/base/nsSocketTransport2.cpp [1991:3448]
void nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status,
nsISupports* param,
std::function<void()>&& task) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
SOCKET_LOG(
("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
" param=%p]\n",
this, type, static_cast<uint32_t>(status), param));
if (NS_FAILED(mCondition)) {
// block event since we're apparently already dead.
SOCKET_LOG((" blocking event [condition=%" PRIx32 "]\n",
static_cast<uint32_t>(mCondition)));
//
// notify input/output streams in case either has a pending notify.
//
mInput->OnSocketReady(mCondition);
mOutput->OnSocketReady(mCondition);
return;
}
switch (type) {
case MSG_ENSURE_CONNECT:
SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));
if (task) {
task();
}
// Apply port remapping here so that we do it on the socket thread and
// before we process the resolved DNS name or create the socket the first
// time.
if (!mPortRemappingApplied) {
mPortRemappingApplied = true;
mSocketTransportService->ApplyPortRemap(&mPort);
mSocketTransportService->ApplyPortRemap(&mOriginPort);
}
//
// ensure that we have created a socket, attached it, and have a
// connection.
//
if (mState == STATE_CLOSED) {
// Unix domain sockets are ready to connect; mNetAddr is all we
// need. Internet address families require a DNS lookup (or possibly
// several) before we can connect.
#if defined(XP_UNIX)
if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL) {
mCondition = InitiateSocket();
} else {
#else
{
#endif
mCondition = ResolveHost();
}
} else {
SOCKET_LOG((" ignoring redundant event\n"));
}
break;
case MSG_DNS_LOOKUP_COMPLETE:
if (mDNSRequest) { // only send this if we actually resolved anything
SendStatus(NS_NET_STATUS_RESOLVED_HOST);
}
SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
mDNSRequest = nullptr;
if (mDNSRecord) {
mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
mDNSRecord->IsTRR(&mResolvedByTRR);
mDNSRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
mDNSRecord->GetTrrSkipReason(&mTRRSkipReason);
}
// status contains DNS lookup status
if (NS_FAILED(status)) {
// When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
// proxy host is not found, so we fixup the error code.
// For SOCKS proxies (mProxyTransparent == true), the socket
// transport resolves the real host here, so there's no fixup
// (see bug 226943).
if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyTransparent &&
!mProxyHost.IsEmpty()) {
mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
} else {
mCondition = status;
}
} else if (mState == STATE_RESOLVING) {
mCondition = InitiateSocket();
}
break;
case MSG_RETRY_INIT_SOCKET:
mCondition = InitiateSocket();
break;
case MSG_INPUT_CLOSED:
SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
OnMsgInputClosed(status);
break;
case MSG_INPUT_PENDING:
SOCKET_LOG((" MSG_INPUT_PENDING\n"));
OnMsgInputPending();
break;
case MSG_OUTPUT_CLOSED:
SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
OnMsgOutputClosed(status);
break;
case MSG_OUTPUT_PENDING:
SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
OnMsgOutputPending();
break;
case MSG_TIMEOUT_CHANGED:
SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
{
MutexAutoLock lock(mLock);
mPollTimeout =
mTimeouts[(mState == STATE_TRANSFERRING) ? TIMEOUT_READ_WRITE
: TIMEOUT_CONNECT];
}
break;
default:
SOCKET_LOG((" unhandled event!\n"));
}
if (NS_FAILED(mCondition)) {
SOCKET_LOG((" after event [this=%p cond=%" PRIx32 "]\n", this,
static_cast<uint32_t>(mCondition)));
if (!mAttached) { // need to process this error ourselves...
OnSocketDetached(nullptr);
}
} else if (mPollFlags == PR_POLL_EXCEPT) {
mPollFlags = 0; // make idle
}
}
uint64_t nsSocketTransport::ByteCountReceived() { return mInput->ByteCount(); }
uint64_t nsSocketTransport::ByteCountSent() { return mOutput->ByteCount(); }
//-----------------------------------------------------------------------------
// socket handler impl
void nsSocketTransport::OnSocketReady(PRFileDesc* fd, int16_t outFlags) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
SOCKET_LOG1(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
this, outFlags));
if (outFlags == -1) {
SOCKET_LOG(("socket timeout expired\n"));
mCondition = NS_ERROR_NET_TIMEOUT;
return;
}
if (mState == STATE_TRANSFERRING) {
// if waiting to write and socket is writable or hit an exception.
if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
// assume that we won't need to poll any longer (the stream will
// request that we poll again if it is still pending).
mPollFlags &= ~PR_POLL_WRITE;
mOutput->OnSocketReady(NS_OK);
}
// if waiting to read and socket is readable or hit an exception.
if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
// assume that we won't need to poll any longer (the stream will
// request that we poll again if it is still pending).
mPollFlags &= ~PR_POLL_READ;
mInput->OnSocketReady(NS_OK);
}
// Update poll timeout in case it was changed
{
MutexAutoLock lock(mLock);
mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
}
} else if ((mState == STATE_CONNECTING) && !gIOService->IsNetTearingDown()) {
// We do not need to do PR_ConnectContinue when we are already
// shutting down.
// We use PRIntervalTime here because we need
// nsIOService::LastOfflineStateChange time and
// nsIOService::LastConectivityChange time to be atomic.
PRIntervalTime connectStarted = 0;
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
connectStarted = PR_IntervalNow();
}
PRStatus status = PR_ConnectContinue(fd, outFlags);
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
connectStarted) {
SendPRBlockingTelemetry(
connectStarted,
glean::networking::prconnectcontinue_blocking_time_normal,
glean::networking::prconnectcontinue_blocking_time_shutdown,
glean::networking::
prconnectcontinue_blocking_time_connectivity_change,
glean::networking::prconnectcontinue_blocking_time_link_change,
glean::networking::prconnectcontinue_blocking_time_offline);
}
if (status == PR_SUCCESS) {
//
// we are connected!
//
OnSocketConnected();
if (mNetAddr.raw.family == AF_INET) {
if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
glean::network::ipv4_and_ipv6_address_connectivity
.AccumulateSingleSample(SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
}
} else if (mNetAddr.raw.family == AF_INET6) {
if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
glean::network::ipv4_and_ipv6_address_connectivity
.AccumulateSingleSample(SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
}
}
} else {
PRErrorCode code = PR_GetError();
#if defined(TEST_CONNECT_ERRORS)
code = RandomizeConnectError(code);
#endif
//
// If the connect is still not ready, then continue polling...
//
if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
// Set up the select flags for connect...
mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
// Update poll timeout in case it was changed
{
MutexAutoLock lock(mLock);
mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
}
}
//
// The SOCKS proxy rejected our request. Find out why.
//
else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
!mProxyHost.IsEmpty()) {
code = PR_GetOSError();
mCondition = ErrorAccordingToNSPR(code);
} else {
//
// else, the connection failed...
//
mCondition = ErrorAccordingToNSPR(code);
if ((mCondition == NS_ERROR_CONNECTION_REFUSED) &&
!mProxyHost.IsEmpty()) {
mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
}
SOCKET_LOG((" connection failed! [reason=%" PRIx32 "]\n",
static_cast<uint32_t>(mCondition)));
}
}
} else if ((mState == STATE_CONNECTING) && gIOService->IsNetTearingDown()) {
// We do not need to do PR_ConnectContinue when we are already
// shutting down.
SOCKET_LOG(
("We are in shutdown so skip PR_ConnectContinue and set "
"and error.\n"));
mCondition = NS_ERROR_ABORT;
} else {
NS_ERROR("unexpected socket state");
mCondition = NS_ERROR_UNEXPECTED;
}
if (mPollFlags == PR_POLL_EXCEPT) mPollFlags = 0; // make idle
}
// called on the socket thread only
void nsSocketTransport::OnSocketDetached(PRFileDesc* fd) {
SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%" PRIx32
"]\n",
this, static_cast<uint32_t>(mCondition)));
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
mAttached = false;
// if we didn't initiate this detach, then be sure to pass an error
// condition up to our consumers. (e.g., STS is shutting down.)
if (NS_SUCCEEDED(mCondition)) {
if (gIOService->IsOffline()) {
mCondition = NS_ERROR_OFFLINE;
} else {
mCondition = NS_ERROR_ABORT;
}
}
// If we are not shutting down try again.
if (!gIOService->IsNetTearingDown() && RecoverFromError()) {
mCondition = NS_OK;
} else {
mState = STATE_CLOSED;
// make sure there isn't any pending DNS request
if (mDNSRequest) {
mDNSRequest->Cancel(NS_ERROR_ABORT);
mDNSRequest = nullptr;
}
//
// notify input/output streams
//
mInput->OnSocketReady(mCondition);
mOutput->OnSocketReady(mCondition);
if (gIOService->IsNetTearingDown()) {
if (mInputCopyContext) {
NS_CancelAsyncCopy(mInputCopyContext, mCondition);
}
if (mOutputCopyContext) {
NS_CancelAsyncCopy(mOutputCopyContext, mCondition);
}
}
}
if (mCondition == NS_ERROR_NET_RESET && mDNSRecord &&
mOutput->ByteCount() == 0) {
// If we are here, it's likely that we are retrying a transaction. Blocking
// the already used address could increase the successful rate of the retry.
mDNSRecord->ReportUnusable(SocketPort());
}
// finally, release our reference to the socket (must do this within
// the transport lock) possibly closing the socket. Also release our
// listeners to break potential refcount cycles.
// We should be careful not to release mEventSink and mCallbacks while
// we're locked, because releasing it might require acquiring the lock
// again, so we just null out mEventSink and mCallbacks while we're
// holding the lock, and let the stack based objects' destuctors take
// care of destroying it if needed.
nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
nsCOMPtr<nsITransportEventSink> ourEventSink;
{
MutexAutoLock lock(mLock);
if (mFD.IsInitialized()) {
ReleaseFD_Locked(mFD);
// flag mFD as unusable; this prevents other consumers from
// acquiring a reference to mFD.
mFDconnected = false;
}
// We must release mCallbacks and mEventSink to avoid memory leak
// but only when RecoverFromError() above failed. Otherwise we lose
// link with UI and security callbacks on next connection attempt
// round. That would lead e.g. to a broken certificate exception page.
if (NS_FAILED(mCondition)) {
mCallbacks.swap(ourCallbacks);
mEventSink.swap(ourEventSink);
}
}
}
void nsSocketTransport::IsLocal(bool* aIsLocal) {
{
MutexAutoLock lock(mLock);
#if defined(XP_UNIX)
// Unix-domain sockets are always local.
if (mNetAddr.raw.family == PR_AF_LOCAL) {
*aIsLocal = true;
return;
}
#endif
*aIsLocal = mNetAddr.IsLoopbackAddr();
}
}
//-----------------------------------------------------------------------------
// xpcom api
NS_IMPL_ISUPPORTS(nsSocketTransport, nsISocketTransport, nsITransport,
nsIDNSListener, nsIClassInfo, nsIInterfaceRequestor)
NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport, nsISocketTransport, nsITransport,
nsIDNSListener, nsIInterfaceRequestor)
NS_IMETHODIMP
nsSocketTransport::OpenInputStream(uint32_t flags, uint32_t segsize,
uint32_t segcount,
nsIInputStream** aResult) {
SOCKET_LOG(
("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags));
NS_ENSURE_TRUE(!mInput->IsReferenced(), NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsIAsyncInputStream> pipeIn;
nsCOMPtr<nsIInputStream> result;
nsCOMPtr<nsISupports> inputCopyContext;
if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
// XXX if the caller wants blocking, then the caller also gets buffered!
// bool openBuffered = !(flags & OPEN_UNBUFFERED);
bool openBlocking = (flags & OPEN_BLOCKING);
net_ResolveSegmentParams(segsize, segcount);
// create a pipe
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), !openBlocking,
true, segsize, segcount);
// async copy from socket to pipe
rv = NS_AsyncCopy(mInput.get(), pipeOut, mSocketTransportService,
NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize, nullptr, nullptr,
true, true, getter_AddRefs(inputCopyContext));
if (NS_FAILED(rv)) return rv;
result = pipeIn;
} else {
result = mInput.get();
}
// flag input stream as open
mInputClosed = false;
// mInputCopyContext can be only touched on socket thread
auto task = [self = RefPtr{this}, inputCopyContext(inputCopyContext)]() {
MOZ_ASSERT(OnSocketThread());
self->mInputCopyContext = inputCopyContext;
};
rv = PostEvent(MSG_ENSURE_CONNECT, NS_OK, nullptr, std::move(task));
if (NS_FAILED(rv)) {
return rv;
}
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::OpenOutputStream(uint32_t flags, uint32_t segsize,
uint32_t segcount,
nsIOutputStream** aResult) {
SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
flags));
NS_ENSURE_TRUE(!mOutput->IsReferenced(), NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
nsCOMPtr<nsIOutputStream> result;
nsCOMPtr<nsISupports> outputCopyContext;
if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
// XXX if the caller wants blocking, then the caller also gets buffered!
// bool openBuffered = !(flags & OPEN_UNBUFFERED);
bool openBlocking = (flags & OPEN_BLOCKING);
net_ResolveSegmentParams(segsize, segcount);
// create a pipe
nsCOMPtr<nsIAsyncInputStream> pipeIn;
NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true,
!openBlocking, segsize, segcount);
// async copy from socket to pipe
rv = NS_AsyncCopy(pipeIn, mOutput.get(), mSocketTransportService,
NS_ASYNCCOPY_VIA_READSEGMENTS, segsize, nullptr, nullptr,
true, true, getter_AddRefs(outputCopyContext));
if (NS_FAILED(rv)) return rv;
result = pipeOut;
} else {
result = mOutput.get();
}
// flag output stream as open
mOutputClosed = false;
// mOutputCopyContext can be only touched on socket thread
auto task = [self = RefPtr{this}, outputCopyContext(outputCopyContext)]() {
MOZ_ASSERT(OnSocketThread());
self->mOutputCopyContext = outputCopyContext;
};
rv = PostEvent(MSG_ENSURE_CONNECT, NS_OK, nullptr, std::move(task));
if (NS_FAILED(rv)) return rv;
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::Close(nsresult reason) {
SOCKET_LOG(("nsSocketTransport::Close %p reason=%" PRIx32, this,
static_cast<uint32_t>(reason)));
if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED;
mDoNotRetryToConnect = true;
mInput->CloseWithStatus(reason);
mOutput->CloseWithStatus(reason);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetTlsSocketControl(nsITLSSocketControl** tlsSocketControl) {
MutexAutoLock lock(mLock);
*tlsSocketControl = do_AddRef(mTLSSocketControl).take();
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor** callbacks) {
MutexAutoLock lock(mLock);
*callbacks = do_AddRef(mCallbacks).take();
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor* callbacks) {
nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
GetCurrentSerialEventTarget(),
getter_AddRefs(threadsafeCallbacks));
MutexAutoLock lock(mLock);
mCallbacks = threadsafeCallbacks;
SOCKET_LOG(("Reset callbacks for tlsSocketInfo=%p callbacks=%p\n",
mTLSSocketControl.get(), mCallbacks.get()));
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetEventSink(nsITransportEventSink* sink,
nsIEventTarget* target) {
nsCOMPtr<nsITransportEventSink> temp;
if (target) {
nsresult rv =
net_NewTransportEventSinkProxy(getter_AddRefs(temp), sink, target);
if (NS_FAILED(rv)) return rv;
sink = temp.get();
}
MutexAutoLock lock(mLock);
mEventSink = sink;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::IsAlive(bool* result) {
*result = false;
nsresult conditionWhileLocked = NS_OK;
PRFileDescAutoLock fd(this, &conditionWhileLocked);
if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
return NS_OK;
}
// XXX do some idle-time based checks??
char c;
int32_t rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR)) {
*result = true;
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetHost(nsACString& host) {
host = SocketHost();
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetPort(int32_t* port) {
*port = (int32_t)SocketPort();
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetScriptableOriginAttributes(
JSContext* aCx, JS::MutableHandle<JS::Value> aOriginAttributes) {
if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetScriptableOriginAttributes(
JSContext* aCx, JS::Handle<JS::Value> aOriginAttributes) {
MutexAutoLock lock(mLock);
NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
OriginAttributes attrs;
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
mOriginAttributes = attrs;
return NS_OK;
}
nsresult nsSocketTransport::GetOriginAttributes(
OriginAttributes* aOriginAttributes) {
NS_ENSURE_ARG(aOriginAttributes);
*aOriginAttributes = mOriginAttributes;
return NS_OK;
}
nsresult nsSocketTransport::SetOriginAttributes(
const OriginAttributes& aOriginAttributes) {
MutexAutoLock lock(mLock);
NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
mOriginAttributes = aOriginAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetPeerAddr(NetAddr* addr) {
// once we are in the connected state, mNetAddr will not change.
// so if we can verify that we are in the connected state, then
// we can freely access mNetAddr from any thread without being
// inside a critical section.
if (!mNetAddrIsSet) {
SOCKET_LOG(
("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
"NOT_AVAILABLE because not yet connected.",
this, mState));
return NS_ERROR_NOT_AVAILABLE;
}
*addr = mNetAddr;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetSelfAddr(NetAddr* addr) {
// once we are in the connected state, mSelfAddr will not change.
// so if we can verify that we are in the connected state, then
// we can freely access mSelfAddr from any thread without being
// inside a critical section.
if (!mSelfAddrIsSet) {
SOCKET_LOG(
("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
"NOT_AVAILABLE because not yet connected.",
this, mState));
return NS_ERROR_NOT_AVAILABLE;
}
*addr = mSelfAddr;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::Bind(NetAddr* aLocalAddr) {
NS_ENSURE_ARG(aLocalAddr);
MutexAutoLock lock(mLock);
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (mAttached) {
return NS_ERROR_FAILURE;
}
mBindAddr = MakeUnique<NetAddr>(*aLocalAddr);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetScriptablePeerAddr(nsINetAddr** addr) {
NetAddr rawAddr;
nsresult rv;
rv = GetPeerAddr(&rawAddr);
if (NS_FAILED(rv)) return rv;
RefPtr<nsNetAddr> netaddr = new nsNetAddr(&rawAddr);
netaddr.forget(addr);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetScriptableSelfAddr(nsINetAddr** addr) {
NetAddr rawAddr;
nsresult rv;
rv = GetSelfAddr(&rawAddr);
if (NS_FAILED(rv)) return rv;
RefPtr<nsNetAddr> netaddr = new nsNetAddr(&rawAddr);
netaddr.forget(addr);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetTimeout(uint32_t type, uint32_t* value) {
NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
MutexAutoLock lock(mLock);
*value = (uint32_t)mTimeouts[type];
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetTimeout(uint32_t type, uint32_t value) {
NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
SOCKET_LOG(("nsSocketTransport::SetTimeout %p type=%u, value=%u", this, type,
value));
// truncate overly large timeout values.
{
MutexAutoLock lock(mLock);
mTimeouts[type] = (uint16_t)std::min<uint32_t>(value, UINT16_MAX);
}
PostEvent(MSG_TIMEOUT_CHANGED);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort) {
mReuseAddrPort = reuseAddrPort;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetLinger(bool aPolarity, int16_t aTimeout) {
MutexAutoLock lock(mLock);
mLingerPolarity = aPolarity;
mLingerTimeout = aTimeout;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetQoSBits(uint8_t aQoSBits) {
// Don't do any checking here of bits. Why? Because as of RFC-4594
// several different Class Selector and Assured Forwarding values
// have been defined, but that isn't to say more won't be added later.
// In that case, any checking would be an impediment to interoperating
// with newer QoS definitions.
mQoSBits = aQoSBits;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetQoSBits(uint8_t* aQoSBits) {
*aQoSBits = mQoSBits;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetRecvBufferSize(uint32_t* aSize) {
PRFileDescAutoLock fd(this);
if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK;
PRSocketOptionData opt;
opt.option = PR_SockOpt_RecvBufferSize;
if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS) {
*aSize = opt.value.recv_buffer_size;
} else {
rv = NS_ERROR_FAILURE;
}
return rv;
}
NS_IMETHODIMP
nsSocketTransport::GetSendBufferSize(uint32_t* aSize) {
PRFileDescAutoLock fd(this);
if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK;
PRSocketOptionData opt;
opt.option = PR_SockOpt_SendBufferSize;
if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS) {
*aSize = opt.value.send_buffer_size;
} else {
rv = NS_ERROR_FAILURE;
}
return rv;
}
NS_IMETHODIMP
nsSocketTransport::SetRecvBufferSize(uint32_t aSize) {
PRFileDescAutoLock fd(this);
if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK;
PRSocketOptionData opt;
opt.option = PR_SockOpt_RecvBufferSize;
opt.value.recv_buffer_size = aSize;
if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS) rv = NS_ERROR_FAILURE;
return rv;
}
NS_IMETHODIMP
nsSocketTransport::SetSendBufferSize(uint32_t aSize) {
PRFileDescAutoLock fd(this);
if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
nsresult rv = NS_OK;
PRSocketOptionData opt;
opt.option = PR_SockOpt_SendBufferSize;
opt.value.send_buffer_size = aSize;
if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS) rv = NS_ERROR_FAILURE;
return rv;
}
NS_IMETHODIMP
nsSocketTransport::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
nsresult status) {
SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
".",
this, static_cast<uint32_t>(status)));
if (NS_SUCCEEDED(status)) {
mDNSRecord = do_QueryInterface(rec);
MOZ_ASSERT(mDNSRecord);
}
if (nsCOMPtr<nsIDNSAddrRecord> addrRecord = do_QueryInterface(rec)) {
addrRecord->IsTRR(&mResolvedByTRR);
addrRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
addrRecord->GetTrrSkipReason(&mTRRSkipReason);
}
// flag host lookup complete for the benefit of the ResolveHost method.
mResolving = false;
nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, nullptr);
// if posting a message fails, then we should assume that the socket
// transport has been shutdown. this should never happen! if it does
// it means that the socket transport service was shutdown before the
// DNS service.
if (NS_FAILED(rv)) {
NS_WARNING("unable to post DNS lookup complete message");
}
return NS_OK;
}
// nsIInterfaceRequestor
NS_IMETHODIMP
nsSocketTransport::GetInterface(const nsIID& iid, void** result) {
if (iid.Equals(NS_GET_IID(nsIDNSRecord)) ||
iid.Equals(NS_GET_IID(nsIDNSAddrRecord))) {
return mDNSRecord ? mDNSRecord->QueryInterface(iid, result)
: NS_ERROR_NO_INTERFACE;
}
return this->QueryInterface(iid, result);
}
NS_IMETHODIMP
nsSocketTransport::GetInterfaces(nsTArray<nsIID>& array) {
return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(array);
}
NS_IMETHODIMP
nsSocketTransport::GetScriptableHelper(nsIXPCScriptable** _retval) {
*_retval = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetContractID(nsACString& aContractID) {
aContractID.SetIsVoid(true);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetClassDescription(nsACString& aClassDescription) {
aClassDescription.SetIsVoid(true);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetClassID(nsCID** aClassID) {
*aClassID = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetFlags(uint32_t* aFlags) {
*aFlags = nsIClassInfo::THREADSAFE;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
nsSocketTransport::GetConnectionFlags(uint32_t* value) {
*value = mConnectionFlags;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetConnectionFlags(uint32_t value) {
SOCKET_LOG(
("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value));
mConnectionFlags = value;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetIsPrivate(bool aIsPrivate) {
mIsPrivate = aIsPrivate;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetTlsFlags(uint32_t* value) {
*value = mTlsFlags;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetTlsFlags(uint32_t value) {
mTlsFlags = value;
return NS_OK;
}
void nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
// The global pref toggles keepalive as a system feature; it only affects
// an individual socket if keepalive has been specifically enabled for it.
// So, ensure keepalive is configured correctly if previously enabled.
if (mKeepaliveEnabled) {
nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
if (NS_WARN_IF(NS_FAILED(rv))) {
SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%" PRIx32 "]",
aEnabled ? "enable" : "disable", static_cast<uint32_t>(rv)));
}
}
}
nsresult nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable) {
MOZ_ASSERT(mKeepaliveIdleTimeS > 0 && mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 &&
mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
mKeepaliveProbeCount <= kMaxTCPKeepCount);
PRFileDescAutoLock fd(this);
if (NS_WARN_IF(!fd.IsInitialized())) {
return NS_ERROR_NOT_INITIALIZED;
}
// Only enable if keepalives are globally enabled, but ensure other
// options are set correctly on the fd.
bool enable = aEnable && mSocketTransportService->IsKeepaliveEnabled();
nsresult rv =
fd.SetKeepaliveVals(enable, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
mKeepaliveProbeCount);
if (NS_WARN_IF(NS_FAILED(rv))) {
SOCKET_LOG((" SetKeepaliveVals failed rv[0x%" PRIx32 "]",
static_cast<uint32_t>(rv)));
return rv;
}
rv = fd.SetKeepaliveEnabled(enable);
if (NS_WARN_IF(NS_FAILED(rv))) {
SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%" PRIx32 "]",
static_cast<uint32_t>(rv)));
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetKeepaliveEnabled(bool* aResult) {
MOZ_ASSERT(aResult);
*aResult = mKeepaliveEnabled;
return NS_OK;
}
nsresult nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
nsresult rv = NS_OK;
int32_t val = -1;
if (mKeepaliveIdleTimeS == -1) {
rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mKeepaliveIdleTimeS = val;
}
if (mKeepaliveRetryIntervalS == -1) {
rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mKeepaliveRetryIntervalS = val;
}
if (mKeepaliveProbeCount == -1) {
rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mKeepaliveProbeCount = val;
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetKeepaliveEnabled(bool aEnable) {
#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (aEnable == mKeepaliveEnabled) {
SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.", this,
aEnable ? "enabled" : "disabled"));
return NS_OK;
}
nsresult rv = NS_OK;
if (aEnable) {
rv = EnsureKeepaliveValsAreInitialized();
if (NS_WARN_IF(NS_FAILED(rv))) {
SOCKET_LOG(
(" SetKeepaliveEnabled [%p] "
"error [0x%" PRIx32 "] initializing keepalive vals",
this, static_cast<uint32_t>(rv)));
return rv;
}
}
SOCKET_LOG(
("nsSocketTransport::SetKeepaliveEnabled [%p] "
"%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
"globally %s.",
this, aEnable ? "enabled" : "disabled", mKeepaliveIdleTimeS,
mKeepaliveRetryIntervalS, mKeepaliveProbeCount,
mSocketTransportService->IsKeepaliveEnabled() ? "enabled" : "disabled"));
// Set mKeepaliveEnabled here so that state is maintained; it is possible
// that we're in between fds, e.g. the 1st IP address failed, so we're about
// to retry on a 2nd from the DNS record.
mKeepaliveEnabled = aEnable;
rv = SetKeepaliveEnabledInternal(aEnable);
if (NS_WARN_IF(NS_FAILED(rv))) {
SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%" PRIx32 "]",
static_cast<uint32_t>(rv)));
return rv;
}
return NS_OK;
#else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
NS_IMETHODIMP
nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime, int32_t aRetryInterval) {
#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(aRetryInterval <= 0 || kMaxTCPKeepIntvl < aRetryInterval)) {
return NS_ERROR_INVALID_ARG;
}
if (aIdleTime == mKeepaliveIdleTimeS &&
aRetryInterval == mKeepaliveRetryIntervalS) {
SOCKET_LOG(
("nsSocketTransport::SetKeepaliveVals [%p] idle time "
"already %ds and retry interval already %ds.",
this, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS));
return NS_OK;
}
mKeepaliveIdleTimeS = aIdleTime;
mKeepaliveRetryIntervalS = aRetryInterval;
nsresult rv = NS_OK;
if (mKeepaliveProbeCount == -1) {
int32_t val = -1;
nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mKeepaliveProbeCount = val;
}
SOCKET_LOG(
("nsSocketTransport::SetKeepaliveVals [%p] "
"keepalive %s, idle time[%ds] retry interval[%ds] "
"packet count[%d]",
this, mKeepaliveEnabled ? "enabled" : "disabled", mKeepaliveIdleTimeS,
mKeepaliveRetryIntervalS, mKeepaliveProbeCount));
PRFileDescAutoLock fd(this);
if (NS_WARN_IF(!fd.IsInitialized())) {
return NS_ERROR_NULL_POINTER;
}
rv = fd.SetKeepaliveVals(mKeepaliveEnabled, mKeepaliveIdleTimeS,
mKeepaliveRetryIntervalS, mKeepaliveProbeCount);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
#else
SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
#ifdef ENABLE_SOCKET_TRACING
# include <stdio.h>
# include <ctype.h>
# include "prenv.h"
static void DumpBytesToFile(const char* path, const char* header,
const char* buf, int32_t n) {
FILE* fp = fopen(path, "a");
fprintf(fp, "\n%s [%d bytes]\n", header, n);
const unsigned char* p;
while (n) {
p = (const unsigned char*)buf;
int32_t i, row_max = std::min(16, n);
for (i = 0; i < row_max; ++i) fprintf(fp, "%02x ", *p++);
for (i = row_max; i < 16; ++i) fprintf(fp, " ");
p = (const unsigned char*)buf;
for (i = 0; i < row_max; ++i, ++p) {
if (isprint(*p))
fprintf(fp, "%c", *p);
else
fprintf(fp, ".");
}
fprintf(fp, "\n");
buf += row_max;
n -= row_max;
}
fprintf(fp, "\n");
fclose(fp);
}
void nsSocketTransport::TraceInBuf(const char* buf, int32_t n) {
char* val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
if (!val || !*val) return;
nsAutoCString header;
header.AssignLiteral("Reading from: ");
header.Append(mHost);
header.Append(':');
header.AppendInt(mPort);
DumpBytesToFile(val, header.get(), buf, n);
}
void nsSocketTransport::TraceOutBuf(const char* buf, int32_t n) {
char* val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
if (!val || !*val) return;
nsAutoCString header;
header.AssignLiteral("Writing to: ");
header.Append(mHost);
header.Append(':');
header.AppendInt(mPort);
DumpBytesToFile(val, header.get(), buf, n);
}
#endif
static void LogNSPRError(const char* aPrefix, const void* aObjPtr) {
#if defined(DEBUG)
PRErrorCode errCode = PR_GetError();
int errLen = PR_GetErrorTextLength();
nsAutoCString errStr;
if (errLen > 0) {
errStr.SetLength(errLen);
PR_GetErrorText(errStr.BeginWriting());
}
NS_WARNING(
nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
errLen > 0 ? errStr.BeginReading() : "<no error text>")
.get());
#endif
}
nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
bool aEnable) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
"Cannot enable keepalive if global pref is disabled!");
if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
return NS_ERROR_ILLEGAL_VALUE;
}
PRSocketOptionData opt;
opt.option = PR_SockOpt_Keepalive;
opt.value.keep_alive = aEnable;
PRStatus status = PR_SetSocketOption(mFd, &opt);
if (NS_WARN_IF(status != PR_SUCCESS)) {
LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
mSocketTransport);
return ErrorAccordingToNSPR(PR_GetError());
}
return NS_OK;
}
static void LogOSError(const char* aPrefix, const void* aObjPtr) {
#if defined(DEBUG)
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
# ifdef XP_WIN
DWORD errCode = WSAGetLastError();
char* errMessage;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&errMessage, 0, NULL);
NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%lx] %s",
aPrefix ? aPrefix : "nsSocketTransport", aObjPtr,
errCode,
errMessage ? errMessage : "<no error text>")
.get());
LocalFree(errMessage);
# else
int errCode = errno;
char* errMessage = strerror(errno);
NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
aPrefix ? aPrefix : "nsSocketTransport", aObjPtr,
errCode,
errMessage ? errMessage : "<no error text>")
.get());
# endif
#endif
}
/* XXX PR_SetSockOpt does not support setting keepalive values, so native
* handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
* file. Requires inclusion of NSPR private/pprio.h, and platform headers.
*/
nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(
bool aEnabled, int aIdleTime, int aRetryInterval, int aProbeCount) {
#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(aRetryInterval <= 0 || kMaxTCPKeepIntvl < aRetryInterval)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
return NS_ERROR_INVALID_ARG;
}
PROsfd sock = PR_FileDesc2NativeHandle(mFd);
if (NS_WARN_IF(sock == -1)) {
LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
mSocketTransport);
return ErrorAccordingToNSPR(PR_GetError());
}
#endif
#if defined(XP_WIN)
// Windows allows idle time and retry interval to be set; NOT ping count.
struct tcp_keepalive keepalive_vals = {(u_long)aEnabled,
// Windows uses msec.
(u_long)(aIdleTime * 1000UL),
(u_long)(aRetryInterval * 1000UL)};
DWORD bytes_returned;
int err =
WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL, NULL);
if (NS_WARN_IF(err)) {
LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
#elif defined(XP_DARWIN)
// Darwin uses sec; only supports idle time being set.
int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &aIdleTime,
sizeof(aIdleTime));
if (NS_WARN_IF(err)) {
LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
mSocketTransport);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
#elif defined(XP_UNIX)
// Not all *nix OSes support the following setsockopt() options
// ... but we assume they are supported in the Android kernel;
// build errors will tell us if they are not.
# if defined(ANDROID) || defined(TCP_KEEPIDLE)
// Idle time until first keepalive probe; interval between ack'd probes;
// seconds.
int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &aIdleTime,
sizeof(aIdleTime));
if (NS_WARN_IF(err)) {
LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
mSocketTransport);
return NS_ERROR_UNEXPECTED;
}
# endif
# if defined(ANDROID) || defined(TCP_KEEPINTVL)
// Interval between unack'd keepalive probes; seconds.
err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &aRetryInterval,
sizeof(aRetryInterval));
if (NS_WARN_IF(err)) {
LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
mSocketTransport);
return NS_ERROR_UNEXPECTED;
}
# endif
# if defined(ANDROID) || defined(TCP_KEEPCNT)
// Number of unack'd keepalive probes before connection times out.
err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &aProbeCount,
sizeof(aProbeCount));
if (NS_WARN_IF(err)) {
LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
mSocketTransport);
return NS_ERROR_UNEXPECTED;
}
# endif
return NS_OK;
#else
MOZ_ASSERT(false,
"nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
"called on unsupported platform!");
return NS_ERROR_UNEXPECTED;
#endif
}
void nsSocketTransport::CloseSocket(PRFileDesc* aFd, bool aTelemetryEnabled) {
#if defined(XP_WIN)
AttachShutdownLayer(aFd);
#endif
// We use PRIntervalTime here because we need
// nsIOService::LastOfflineStateChange time and
// nsIOService::LastConectivityChange time to be atomic.
PRIntervalTime closeStarted;
if (aTelemetryEnabled) {
closeStarted = PR_IntervalNow();
}
PR_Close(aFd);
if (aTelemetryEnabled) {
SendPRBlockingTelemetry(
closeStarted, glean::networking::prclose_tcp_blocking_time_normal,
glean::networking::prclose_tcp_blocking_time_shutdown,
glean::networking::prclose_tcp_blocking_time_connectivity_change,
glean::networking::prclose_tcp_blocking_time_link_change,
glean::networking::prclose_tcp_blocking_time_offline);
}
}
void nsSocketTransport::SendPRBlockingTelemetry(
PRIntervalTime aStart,
const glean::impl::TimingDistributionMetric& aMetricNormal,
const glean::impl::TimingDistributionMetric& aMetricShutdown,
const glean::impl::TimingDistributionMetric& aMetricConnectivityChange,
const glean::impl::TimingDistributionMetric& aMetricLinkChange,
const glean::impl::TimingDistributionMetric& aMetricOffline) {
PRIntervalTime now = PR_IntervalNow();
TimeDuration delta =
TimeDuration::FromMilliseconds(PR_IntervalToMilliseconds(now - aStart));
if (gIOService->IsNetTearingDown()) {
aMetricShutdown.AccumulateRawDuration(delta);
} else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange()) <
60) {
aMetricConnectivityChange.AccumulateRawDuration(delta);
} else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange()) <
60) {
aMetricLinkChange.AccumulateRawDuration(delta);
} else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange()) <
60) {
aMetricOffline.AccumulateRawDuration(delta);
} else {
aMetricNormal.AccumulateRawDuration(delta);
}
}
NS_IMETHODIMP
nsSocketTransport::GetResetIPFamilyPreference(bool* aReset) {
*aReset = mResetFamilyPreference;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetEchConfigUsed(bool* aEchConfigUsed) {
*aEchConfigUsed = mEchConfigUsed;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetEchConfig(const nsACString& aEchConfig) {
mEchConfig = aEchConfig;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::ResolvedByTRR(bool* aResolvedByTRR) {
*aResolvedByTRR = mResolvedByTRR;
return NS_OK;
}
NS_IMETHODIMP nsSocketTransport::GetEffectiveTRRMode(
nsIRequest::TRRMode* aEffectiveTRRMode) {
*aEffectiveTRRMode = mEffectiveTRRMode;
return NS_OK;
}
NS_IMETHODIMP nsSocketTransport::GetTrrSkipReason(
nsITRRSkipReason::value* aSkipReason) {
*aSkipReason = mTRRSkipReason;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetRetryDnsIfPossible(bool* aRetryDns) {
*aRetryDns = mRetryDnsIfPossible;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetStatus(nsresult* aStatus) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
*aStatus = mCondition;
return NS_OK;
}
} // namespace net