Bug 1690615 - Move DNS lookup into DnsAndConnectSocket r=necko-reviewers,valentin

Differential Revision: https://phabricator.services.mozilla.com/D104464
This commit is contained in:
Dragana Damjanovic 2021-02-12 14:57:09 +00:00
parent 169e0b5a38
commit efa5705d23
32 changed files with 753 additions and 159 deletions

View File

@ -270,9 +270,9 @@ var _attemptTransport = async function(settings) {
var _attemptConnect = async function({ host, port, encryption }) {
let s;
if (encryption) {
s = socketTransportService.createTransport(["ssl"], host, port, null);
s = socketTransportService.createTransport(["ssl"], host, port, null, null);
} else {
s = socketTransportService.createTransport([], host, port, null);
s = socketTransportService.createTransport([], host, port, null, null);
}
// Force disabling IPV6 if we aren't explicitely connecting to an IPv6 address

View File

@ -302,7 +302,7 @@ void WebrtcTCPSocket::OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo) {
nsCOMPtr<nsISocketTransportService> sts =
do_GetService("@mozilla.org/network/socket-transport-service;1");
rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo,
rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo, nullptr,
getter_AddRefs(mTransport));
if (NS_WARN_IF(NS_FAILED(rv))) {
CloseWithReason(rv);

View File

@ -185,6 +185,10 @@ class FakeSocketTransportProvider : public nsISocketTransport {
MOZ_ASSERT(false);
return NS_OK;
}
NS_IMETHOD GetRetryDnsIfPossible(bool* aRetryDns) override {
MOZ_ASSERT(false);
return NS_OK;
}
// nsITransport
NS_IMETHOD OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize,

View File

@ -252,8 +252,9 @@ nsresult TCPSocket::Init() {
socketTypes.AppendElement("starttls"_ns);
}
nsCOMPtr<nsISocketTransport> transport;
nsresult rv = sts->CreateTransport(socketTypes, NS_ConvertUTF16toUTF8(mHost),
mPort, nullptr, getter_AddRefs(transport));
nsresult rv =
sts->CreateTransport(socketTypes, NS_ConvertUTF16toUTF8(mHost), mPort,
nullptr, nullptr, getter_AddRefs(transport));
NS_ENSURE_SUCCESS(rv, rv);
return InitWithUnconnectedTransport(transport);

View File

@ -210,5 +210,10 @@ MockedSocketTransport::GetQoSBits(uint8_t*) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP
MockedSocketTransport::SetQoSBits(uint8_t) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP
MockedSocketTransport::GetRetryDnsIfPossible(bool*) {
return NS_ERROR_NOT_IMPLEMENTED;
}
} // namespace dom
} // namespace mozilla

View File

@ -170,7 +170,7 @@ PresentationTCPSessionTransport::BuildTCPReceiverTransport(
return NS_ERROR_NOT_AVAILABLE;
}
rv = sts->CreateTransport(nsTArray<nsCString>(), serverHost, serverPort,
nullptr, getter_AddRefs(mTransport));
nullptr, nullptr, getter_AddRefs(mTransport));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -220,6 +220,7 @@ PresentationControlService.prototype = {
["ssl"],
aDeviceInfo.address,
aDeviceInfo.port,
null,
null
);
} else {
@ -227,6 +228,7 @@ PresentationControlService.prototype = {
[],
aDeviceInfo.address,
aDeviceInfo.port,
null,
null
);
}

View File

@ -1120,11 +1120,11 @@ nsresult Dashboard::TestNewConnection(ConnectionData* aConnectionData) {
AutoTArray<nsCString, 1> socketTypes = {connectionData->mProtocol};
rv = gSocketTransportService->CreateTransport(
socketTypes, connectionData->mHost, connectionData->mPort, nullptr,
getter_AddRefs(connectionData->mSocket));
nullptr, getter_AddRefs(connectionData->mSocket));
} else {
rv = gSocketTransportService->CreateTransport(
nsTArray<nsCString>(), connectionData->mHost, connectionData->mPort,
nullptr, getter_AddRefs(connectionData->mSocket));
nullptr, nullptr, getter_AddRefs(connectionData->mSocket));
}
if (NS_FAILED(rv)) {
return rv;

View File

@ -340,4 +340,12 @@ interface nsISocketTransport : nsITransport
* this socket will be closed when the last private window is closed.
*/
[noscript] void setIsPrivate(in boolean isPrivate);
/**
* If DNS is performed externally, this flag informs the caller that it may
* retry connecting with a different DNS configuration (e.g. different IP
* family preference). The flag is set only if a network error is encounder,
* e.g. NS_ERROR_CONNECTION_REFUSED, NS_ERROR_RESET, etc.
*/
readonly attribute boolean retryDnsIfPossible;
};

View File

@ -5,6 +5,7 @@
#include "nsISupports.idl"
interface nsIDNSRecord;
interface nsIFile;
interface nsISocketTransport;
interface nsIProxyInfo;
@ -46,6 +47,8 @@ interface nsISocketTransportService : nsISupports
* specifies the transport-layer proxy type to use. null if no
* proxy. used for communicating information about proxies like
* SOCKS (which are transparent to upper protocols).
* @param aDnsRecord
* the dns record to be used for the connection
*
* @see nsIProxiedProtocolHandler
* @see nsIProtocolProxyService::GetProxyInfo
@ -55,7 +58,8 @@ interface nsISocketTransportService : nsISupports
nsISocketTransport createTransport(in Array<ACString> aSocketTypes,
in AUTF8String aHost,
in long aPort,
in nsIProxyInfo aProxyInfo);
in nsIProxyInfo aProxyInfo,
in nsIDNSRecord dnsRecord);
/**
* Create a transport built on a Unix domain socket, connecting to the
@ -153,5 +157,6 @@ interface nsIRoutedSocketTransportService : nsISocketTransportService
in long aPort, // origin
in AUTF8String aHostRoute,
in long aPortRoute,
in nsIProxyInfo aProxyInfo);
in nsIProxyInfo aProxyInfo,
in nsIDNSRecord aDnsRecord);
};

View File

@ -727,13 +727,19 @@ nsresult nsSocketTransport::Init(const nsTArray<nsCString>& types,
const nsACString& host, uint16_t port,
const nsACString& hostRoute,
uint16_t portRoute,
nsIProxyInfo* givenProxyInfo) {
nsIProxyInfo* givenProxyInfo,
nsIDNSRecord* dnsRecord) {
nsCOMPtr<nsProxyInfo> proxyInfo;
if (givenProxyInfo) {
proxyInfo = do_QueryInterface(givenProxyInfo);
NS_ENSURE_ARG(proxyInfo);
}
if (dnsRecord) {
mExternalDNSResolution = true;
mDNSRecord = do_QueryInterface(dnsRecord);
}
// init socket type info
mOriginHost = host;
@ -990,6 +996,12 @@ nsresult nsSocketTransport::ResolveHost() {
}
}
if (mExternalDNSResolution) {
MOZ_ASSERT(mDNSRecord);
mState = STATE_RESOLVING;
return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
}
nsCOMPtr<nsIDNSService> dns = nullptr;
auto initTask = [&dns]() { dns = do_GetService(kDNSServiceCID); };
if (!NS_IsMainThread()) {
@ -1688,8 +1700,6 @@ bool nsSocketTransport::RecoverFromError() {
mDNSRecord->ReportUnusable(SocketPort());
}
#if defined(_WIN64) && defined(WIN95)
// can only recover from these errors
if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
mCondition != NS_ERROR_NET_TIMEOUT &&
@ -1699,17 +1709,6 @@ bool nsSocketTransport::RecoverFromError() {
static_cast<uint32_t>(mCondition)));
return false;
}
#else
if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
mCondition != NS_ERROR_NET_TIMEOUT &&
mCondition != NS_ERROR_UNKNOWN_HOST &&
mCondition != NS_ERROR_UNKNOWN_PROXY_HOST) {
SOCKET_LOG((" not a recoverable error %" PRIx32,
static_cast<uint32_t>(mCondition)));
return false;
}
#endif
bool tryAgain = false;
@ -1746,6 +1745,19 @@ bool nsSocketTransport::RecoverFromError() {
if (NS_SUCCEEDED(rv)) {
SOCKET_LOG((" trying again with next ip address\n"));
tryAgain = true;
} else if (mExternalDNSResolution) {
mRetryDnsIfPossible = true;
bool trrEnabled;
mDNSRecord->IsTRR(&trrEnabled);
// Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
// should intentionally not fallback to regular DNS.
if (trrEnabled && !StaticPrefs::network_trr_fallback_on_zero_response() &&
((mNetAddr.raw.family == AF_INET && mNetAddr.inet.ip == 0) ||
(mNetAddr.raw.family == AF_INET6 && mNetAddr.inet6.ip.u64[0] == 0 &&
mNetAddr.inet6.ip.u64[1] == 0))) {
SOCKET_LOG((" TRR returned 0.0.0.0 and there are no other IPs"));
mRetryDnsIfPossible = false;
}
} else if (mConnectionFlags & RETRY_WITH_DIFFERENT_IP_FAMILY) {
SOCKET_LOG((" failed to connect, trying with opposite ip family\n"));
// Drop state to closed. This will trigger new round of DNS
@ -3356,5 +3368,11 @@ nsSocketTransport::ResolvedByTRR(bool* aResolvedByTRR) {
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetRetryDnsIfPossible(bool* aRetryDns) {
*aRetryDns = mRetryDnsIfPossible;
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -127,7 +127,7 @@ class nsSocketTransport final : public nsASocketHandler,
// given type(s) to the given host or proxy.
nsresult Init(const nsTArray<nsCString>& socketTypes, const nsACString& host,
uint16_t port, const nsACString& hostRoute, uint16_t portRoute,
nsIProxyInfo* proxyInfo);
nsIProxyInfo* proxyInfo, nsIDNSRecord* dnsRecord);
// this method instructs the socket transport to use an already connected
// socket with the given address.
@ -461,6 +461,9 @@ class nsSocketTransport final : public nsASocketHandler,
// Whether the port remapping has already been applied. We definitely want to
// prevent duplicate calls in case of chaining remapping.
bool mPortRemappingApplied = false;
bool mExternalDNSResolution = false;
bool mRetryDnsIfPossible = false;
};
} // namespace net

View File

@ -944,20 +944,23 @@ NS_IMETHODIMP
nsSocketTransportService::CreateTransport(const nsTArray<nsCString>& types,
const nsACString& host, int32_t port,
nsIProxyInfo* proxyInfo,
nsIDNSRecord* dnsRecord,
nsISocketTransport** result) {
return CreateRoutedTransport(types, host, port, ""_ns, 0, proxyInfo, result);
return CreateRoutedTransport(types, host, port, ""_ns, 0, proxyInfo,
dnsRecord, result);
}
NS_IMETHODIMP
nsSocketTransportService::CreateRoutedTransport(
const nsTArray<nsCString>& types, const nsACString& host, int32_t port,
const nsACString& hostRoute, int32_t portRoute, nsIProxyInfo* proxyInfo,
nsISocketTransport** result) {
nsIDNSRecord* dnsRecord, nsISocketTransport** result) {
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
RefPtr<nsSocketTransport> trans = new nsSocketTransport();
nsresult rv = trans->Init(types, host, port, hostRoute, portRoute, proxyInfo);
nsresult rv = trans->Init(types, host, port, hostRoute, portRoute, proxyInfo,
dnsRecord);
if (NS_FAILED(rv)) {
return rv;
}

View File

@ -1367,7 +1367,7 @@ nsFtpState::R_pasv() {
}
rv = sts->CreateTransport(nsTArray<nsCString>(), host, port,
mChannel->ProxyInfo(),
mChannel->ProxyInfo(), nullptr,
getter_AddRefs(strans)); // the data socket
if (NS_FAILED(rv)) return FTP_ERROR;
mDataTransport = strans;

View File

@ -94,6 +94,7 @@ nsresult nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
if (NS_FAILED(rv)) return rv;
rv = sts->CreateTransport(nsTArray<nsCString>(), mHost, mPort, proxyInfo,
nullptr,
getter_AddRefs(mSocket)); // the command transport
if (NS_FAILED(rv)) return rv;

View File

@ -12,6 +12,9 @@
#include "nsIDNSRecord.h"
#include "nsIDNSService.h"
#include "nsQueryObject.h"
#include "nsURLHelper.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/SyncRunnable.h"
// Log on level :5, instead of default :4.
#undef LOG
@ -33,6 +36,7 @@ NS_INTERFACE_MAP_BEGIN(DnsAndConnectSocket)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
NS_INTERFACE_MAP_ENTRY_CONCRETE(DnsAndConnectSocket)
NS_INTERFACE_MAP_END
@ -41,11 +45,13 @@ DnsAndConnectSocket::DnsAndConnectSocket(ConnectionEntry* ent,
uint32_t caps, bool speculative,
bool isFromPredictor, bool urgentStart)
: mTransaction(trans),
mPrimaryTransport(false),
mCaps(caps),
mSpeculative(speculative),
mUrgentStart(urgentStart),
mIsFromPredictor(isFromPredictor),
mEnt(ent) {
mEnt(ent),
mBackupTransport(true) {
MOZ_ASSERT(ent && trans, "constructor with null arguments");
LOG(("Creating DnsAndConnectSocket [this=%p trans=%p ent=%s key=%s]\n", this,
trans, ent->mConnInfo->Origin(), ent->mConnInfo->HashKey().get()));
@ -67,9 +73,10 @@ DnsAndConnectSocket::DnsAndConnectSocket(ConnectionEntry* ent,
}
DnsAndConnectSocket::~DnsAndConnectSocket() {
LOG(("Destroying DnsAndConnectSocket [this=%p]\n", this));
MOZ_ASSERT(!mPrimaryTransport.mSocketTransport);
MOZ_ASSERT(!mBackupTransport.mSocketTransport);
LOG(("Destroying DnsAndConnectSocket [this=%p]\n", this));
MOZ_ASSERT(mState == DnsAndSocketState::DONE);
if (mEnt) {
bool inqueue = mEnt->RemoveDnsAndConnectSocket(this);
@ -79,27 +86,181 @@ DnsAndConnectSocket::~DnsAndConnectSocket() {
}
}
nsresult DnsAndConnectSocket::SetupPrimaryStreams() {
nsresult DnsAndConnectSocket::Init() {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(mEnt);
MOZ_ASSERT(mState == DnsAndSocketState::INIT);
nsresult rv = mPrimaryTransport.SetupStreams(this, false);
if (mEnt->mConnInfo->GetRoutedHost().IsEmpty()) {
mPrimaryTransport.mHost = mEnt->mConnInfo->GetOrigin();
mBackupTransport.mHost = mEnt->mConnInfo->GetOrigin();
} else {
mPrimaryTransport.mHost = mEnt->mConnInfo->GetRoutedHost();
mBackupTransport.mHost = mEnt->mConnInfo->GetRoutedHost();
}
LOG(("DnsAndConnectSocket::SetupPrimaryStream [this=%p ent=%s rv=%" PRIx32
"]",
this, mEnt->mConnInfo->Origin(), static_cast<uint32_t>(rv)));
CheckProxyConfig();
return rv;
if (!mSkipDnsResolution) {
nsresult rv = SetupDnsFlags();
NS_ENSURE_SUCCESS(rv, rv);
}
return SetupEvent(SetupEvents::INIT_EVENT);
}
nsresult DnsAndConnectSocket::SetupBackupStreams() {
MOZ_ASSERT(mTransaction);
void DnsAndConnectSocket::CheckProxyConfig() {
const nsHttpConnectionInfo* ci = mEnt->mConnInfo;
nsresult rv = mBackupTransport.SetupStreams(this, true);
if (ci->ProxyInfo()) {
nsCOMPtr<nsProxyInfo> proxyInfo = ci->ProxyInfo();
nsAutoCString proxyType(proxyInfo->Type());
LOG(("DnsAndConnectSocket::SetupBackupStream [this=%p ent=%s rv=%" PRIx32 "]",
this, mEnt->mConnInfo->Origin(), static_cast<uint32_t>(rv)));
bool proxyTransparent = false;
if (proxyType.EqualsLiteral("socks") || proxyType.EqualsLiteral("socks4")) {
proxyTransparent = true;
if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
mProxyTransparentResolvesHost = true;
}
}
return rv;
if (mProxyTransparentResolvesHost) {
// Name resolution is done on the server side. Just pretend
// client resolution is complete, this will get picked up later.
// since we don't need to do DNS now, we bypass the resolving
// step by initializing mNetAddr to an empty address, but we
// must keep the port. The SOCKS IO layer will use the hostname
// we send it when it's created, rather than the empty address
// we send with the connect call.
mPrimaryTransport.mSkipDnsResolution = true;
mBackupTransport.mSkipDnsResolution = true;
mSkipDnsResolution = true;
}
if (!proxyTransparent && !proxyInfo->Host().IsEmpty()) {
mProxyNotTransparent = true;
mPrimaryTransport.mHost = proxyInfo->Host();
mBackupTransport.mHost = proxyInfo->Host();
}
}
}
nsresult DnsAndConnectSocket::SetupDnsFlags() {
LOG(("DnsAndConnectSocket::SetupDnsFlags [this=%p] ", this));
uint32_t dnsFlags = 0;
if (mCaps & NS_HTTP_REFRESH_DNS) {
dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
}
if (mCaps & NS_HTTP_DISABLE_IPV4) {
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
} else if (mCaps & NS_HTTP_DISABLE_IPV6) {
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
} else if (mEnt->PreferenceKnown()) {
if (mEnt->mPreferIPv6) {
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
} else if (mEnt->mPreferIPv4) {
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
}
mPrimaryTransport.mRetryWithDifferentIPFamily = true;
mBackupTransport.mRetryWithDifferentIPFamily = true;
}
if (mEnt->mConnInfo->HasIPHintAddress()) {
nsresult rv;
nsCOMPtr<nsIDNSService> dns =
do_GetService("@mozilla.org/network/dns-service;1", &rv);
if (NS_FAILED(rv)) {
return rv;
}
// The spec says: "If A and AAAA records for TargetName are locally
// available, the client SHOULD ignore these hints.", so we check if the DNS
// record is in cache before setting USE_IP_HINT_ADDRESS.
nsCOMPtr<nsIDNSRecord> record;
rv = dns->ResolveNative(
mPrimaryTransport.mHost, nsIDNSService::RESOLVE_OFFLINE,
mEnt->mConnInfo->GetOriginAttributes(), getter_AddRefs(record));
if (NS_FAILED(rv) || !record) {
LOG(("Setting Socket to use IP hint address"));
dnsFlags |= nsIDNSService::RESOLVE_IP_HINT;
}
}
dnsFlags |=
nsIDNSService::GetFlagsFromTRRMode(NS_HTTP_TRR_MODE_FROM_FLAGS(mCaps));
// When we get here, we are not resolving using any configured proxy likely
// because of individual proxy setting on the request or because the host is
// excluded from proxying. Hence, force resolution despite global proxy-DNS
// configuration.
dnsFlags |= nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS;
NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
"Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
mPrimaryTransport.mDnsFlags = dnsFlags;
mBackupTransport.mDnsFlags = dnsFlags;
return NS_OK;
}
nsresult DnsAndConnectSocket::SetupEvent(SetupEvents event) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
LOG(("DnsAndConnectSocket::SetupEvent state=%d event=%d", mState, event));
switch (event) {
case SetupEvents::INIT_EVENT: {
MOZ_ASSERT(mState == DnsAndSocketState::INIT);
nsresult rv = mPrimaryTransport.Init(this);
if (NS_FAILED(rv)) {
mState = DnsAndSocketState::DONE;
return rv;
}
if (mPrimaryTransport.FirstResolving()) {
mState = DnsAndSocketState::RESOLVING;
} else if (mPrimaryTransport.ConnectingOrRetry()) {
mState = DnsAndSocketState::CONNECTING;
SetupBackupTimer();
} else {
MOZ_ASSERT(false);
return NS_ERROR_UNEXPECTED;
}
} break;
case SetupEvents::RESOLVED_PRIMARY_EVENT:
// This eventt may be posted multiple times if a DNS lookup is
// retriggered, e.g with different parameter.
if (mState == DnsAndSocketState::RESOLVING) {
mState = DnsAndSocketState::CONNECTING;
SetupBackupTimer();
}
break;
case SetupEvents::PRIMARY_DONE_EVENT:
MOZ_ASSERT((mState == DnsAndSocketState::RESOLVING) ||
(mState == DnsAndSocketState::CONNECTING) ||
(mState == DnsAndSocketState::ONE_CONNECTED));
CancelBackupTimer();
mBackupTransport.CancelDnsResolution();
if (mBackupTransport.ConnectingOrRetry()) {
mState = DnsAndSocketState::ONE_CONNECTED;
} else {
mState = DnsAndSocketState::DONE;
}
break;
case SetupEvents::BACKUP_DONE_EVENT:
MOZ_ASSERT((mState == DnsAndSocketState::CONNECTING) ||
(mState == DnsAndSocketState::ONE_CONNECTED));
if (mPrimaryTransport.ConnectingOrRetry()) {
mState = DnsAndSocketState::ONE_CONNECTED;
} else {
mState = DnsAndSocketState::DONE;
}
break;
case SetupEvents::BACKUP_TIMER_FIRED_EVENT:
MOZ_ASSERT(mState == DnsAndSocketState::CONNECTING);
mBackupTransport.Init(this);
}
LOG(("DnsAndConnectSocket::SetupEvent state=%d", mState));
return NS_OK;
}
void DnsAndConnectSocket::SetupBackupTimer() {
@ -109,7 +270,7 @@ void DnsAndConnectSocket::SetupBackupTimer() {
// When using Fast Open the correct transport will be setup for sure (it is
// guaranteed), but it can be that it will happened a bit later.
if (timeout && !mSpeculative) {
if (timeout && !mSpeculative && !mIsHttp3) {
// Setup the timer that will establish a backup socket
// if we do not get a writable event on the main one.
// We do this because a lost SYN takes a very long time
@ -157,6 +318,8 @@ void DnsAndConnectSocket::Abandon() {
// Stop the timer - we don't want any new backups.
CancelBackupTimer();
mState = DnsAndSocketState::DONE;
// Remove the half open from the connection entry.
if (mEnt) {
mEnt->mDoNotDestroy = false;
@ -178,11 +341,12 @@ DnsAndConnectSocket::Notify(nsITimer* timer) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(timer == mSynTimer, "wrong timer");
MOZ_ASSERT(!mBackupTransport.mDNSRequest);
MOZ_ASSERT(!mBackupTransport.mSocketTransport);
MOZ_ASSERT(mSynTimer);
MOZ_ASSERT(mEnt);
DebugOnly<nsresult> rv = SetupBackupStreams();
DebugOnly<nsresult> rv = SetupEvent(BACKUP_TIMER_FIRED_EVENT);
MOZ_ASSERT(NS_SUCCEEDED(rv));
// Keeping the reference to the timer to remember we have already
@ -197,6 +361,50 @@ DnsAndConnectSocket::GetName(nsACString& aName) {
return NS_OK;
}
NS_IMETHODIMP
DnsAndConnectSocket::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
nsresult status) {
LOG(("DnsAndConnectSocket::OnLookupComplete: this=%p status %" PRIx32 ".",
this, static_cast<uint32_t>(status)));
RefPtr<DnsAndConnectSocket> deleteProtector(this);
if (!IsPrimary(request) && !IsBackup(request)) {
return NS_OK;
}
if (IsPrimary(request) && NS_SUCCEEDED(status)) {
mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RESOLVED_HOST, 0);
}
// 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 (mProxyNotTransparent && (status == NS_ERROR_UNKNOWN_HOST)) {
status = NS_ERROR_UNKNOWN_PROXY_HOST;
}
nsresult rv;
// remember if it was primary because TransportSetups will delete the ref to
// the DNS request and check cannot be performed later.
bool isPrimary = IsPrimary(request);
if (IsPrimary(request)) {
rv = mPrimaryTransport.OnLookupComplete(this, rec, status);
if (mPrimaryTransport.ConnectingOrRetry()) {
SetupEvent(SetupEvents::RESOLVED_PRIMARY_EVENT);
}
} else {
rv = mBackupTransport.OnLookupComplete(this, rec, status);
}
if (NS_FAILED(rv)) {
SetupConn(isPrimary, rv);
}
return NS_OK;
}
// method for nsIAsyncOutputStreamCallback
NS_IMETHODIMP
DnsAndConnectSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) {
@ -206,33 +414,68 @@ DnsAndConnectSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) {
MOZ_ASSERT(IsPrimary(out) || IsBackup(out), "stream mismatch");
MOZ_ASSERT(mEnt);
RefPtr<DnsAndConnectSocket> deleteProtector(this);
LOG(("DnsAndConnectSocket::OnOutputStreamReady [this=%p ent=%s %s]\n", this,
mEnt->mConnInfo->Origin(), IsPrimary(out) ? "primary" : "backup"));
// Remember if it was primary or backup reuest.
bool isPrimary = IsPrimary(out);
nsresult rv = NS_OK;
if (isPrimary) {
rv = mPrimaryTransport.CheckConnectedResult(this);
if (!mPrimaryTransport.DoneConnecting()) {
return NS_OK;
}
MOZ_ASSERT((NS_SUCCEEDED(rv) &&
(mPrimaryTransport.mState ==
TransportSetup::TransportSetupState::CONNECTING_DONE) &&
mPrimaryTransport.mSocketTransport) ||
(NS_FAILED(rv) &&
(mPrimaryTransport.mState ==
TransportSetup::TransportSetupState::DONE) &&
!mPrimaryTransport.mSocketTransport));
} else if (IsBackup(out)) {
rv = mBackupTransport.CheckConnectedResult(this);
if (!mBackupTransport.DoneConnecting()) {
return NS_OK;
}
MOZ_ASSERT((NS_SUCCEEDED(rv) &&
(mBackupTransport.mState ==
TransportSetup::TransportSetupState::CONNECTING_DONE) &&
mBackupTransport.mSocketTransport) ||
(NS_FAILED(rv) &&
(mBackupTransport.mState ==
TransportSetup::TransportSetupState::DONE) &&
!mBackupTransport.mSocketTransport));
} else {
MOZ_ASSERT(false, "unexpected stream");
return NS_ERROR_UNEXPECTED;
}
mEnt->mDoNotDestroy = true;
gHttpHandler->ConnMgr()->RecvdConnect();
CancelBackupTimer();
nsresult rv = SetupConn(out);
rv = SetupConn(isPrimary, rv);
if (mEnt) {
mEnt->mDoNotDestroy = false;
}
return rv;
}
nsresult DnsAndConnectSocket::SetupConn(nsIAsyncOutputStream* out) {
nsresult DnsAndConnectSocket::SetupConn(bool isPrimary, nsresult status) {
// assign the new socket to the http connection
RefPtr<HttpConnectionBase> conn;
nsresult rv = NS_OK;
if (IsPrimary(out)) {
rv = mPrimaryTransport.SetupConn(mTransaction, mEnt, getter_AddRefs(conn));
} else if (IsBackup(out)) {
rv = mBackupTransport.SetupConn(mTransaction, mEnt, getter_AddRefs(conn));
if (isPrimary) {
SetupEvent(SetupEvents::PRIMARY_DONE_EVENT);
rv = mPrimaryTransport.SetupConn(mTransaction, mEnt, status,
getter_AddRefs(conn));
} else {
MOZ_ASSERT(false, "unexpected stream");
rv = NS_ERROR_UNEXPECTED;
SetupEvent(SetupEvents::BACKUP_DONE_EVENT);
rv = mBackupTransport.SetupConn(mTransaction, mEnt, status,
getter_AddRefs(conn));
}
nsCOMPtr<nsIInterfaceRequestor> callbacks;
@ -390,12 +633,13 @@ DnsAndConnectSocket::OnTransportStatus(nsITransport* trans, nsresult status,
// Send this status event only if the transaction is still pending,
// i.e. it has not found a free already connected socket.
// Sockets in halfOpen state can only get following events:
// NS_NET_STATUS_RESOLVING_HOST, NS_NET_STATUS_RESOLVED_HOST,
// NS_NET_STATUS_CONNECTING_TO and NS_NET_STATUS_CONNECTED_TO.
// mBackupTransport is only started after
// NS_NET_STATUS_CONNECTING_TO of mSocketTransport, so ignore all
// mBackupTransport events until NS_NET_STATUS_CONNECTED_TO.
// mBackupTransport must be connected before mSocketTransport.
// NS_NET_STATUS_CONNECTING_TO of mSocketTransport, so ignore
// NS_NET_STATUS_CONNECTING_TO event for mBackupTransport and
// send NS_NET_STATUS_CONNECTED_TO.
// mBackupTransport must be connected before mSocketTransport(e.g.
// mPrimaryTransport.mSocketTransport != nullpttr).
mTransaction->OnTransportStatus(trans, status, progress);
}
}
@ -413,8 +657,6 @@ DnsAndConnectSocket::OnTransportStatus(nsITransport* trans, nsresult status,
return NS_OK;
}
mPrimaryStreamStatus = status;
// if we are doing spdy coalescing and haven't recorded the ip address
// for this entry before then make the hash key if our dns lookup
// just completed. We can't do coalescing if using a proxy because the
@ -471,34 +713,6 @@ DnsAndConnectSocket::OnTransportStatus(nsITransport* trans, nsresult status,
}
}
switch (status) {
case NS_NET_STATUS_CONNECTING_TO:
// Passed DNS resolution, now trying to connect, start the backup timer
// only prevent creating another backup transport.
// We also check for mEnt presence to not instantiate the timer after
// this half open socket has already been abandoned. It may happen
// when we get this notification right between main-thread calls to
// nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
// where the first abandons all half open socket instances and only
// after that the second stops the socket thread.
// Http3 has its own syn-retransmission, therefore it does not need a
// backup connection.
if (mEnt && !mBackupTransport.mSocketTransport && !mSynTimer &&
!mIsHttp3) {
SetupBackupTimer();
}
break;
case NS_NET_STATUS_CONNECTED_TO:
// TCP connection's up, now transfer or SSL negotiantion starts,
// no need for backup socket
CancelBackupTimer();
break;
default:
break;
}
return NS_OK;
}
@ -510,6 +724,10 @@ bool DnsAndConnectSocket::IsPrimary(nsIAsyncOutputStream* out) {
return out == mPrimaryTransport.mStreamOut;
}
bool DnsAndConnectSocket::IsPrimary(nsICancelable* dnsRequest) {
return dnsRequest == mPrimaryTransport.mDNSRequest;
}
bool DnsAndConnectSocket::IsBackup(nsITransport* trans) {
return trans == mBackupTransport.mSocketTransport;
}
@ -518,6 +736,10 @@ bool DnsAndConnectSocket::IsBackup(nsIAsyncOutputStream* out) {
return out == mBackupTransport.mStreamOut;
}
bool DnsAndConnectSocket::IsBackup(nsICancelable* dnsRequest) {
return dnsRequest == mBackupTransport.mDNSRequest;
}
// method for nsIInterfaceRequestor
NS_IMETHODIMP
DnsAndConnectSocket::GetInterface(const nsIID& iid, void** result) {
@ -560,7 +782,7 @@ bool DnsAndConnectSocket::Claim() {
// Http3 has its own syn-retransmission, therefore it does not need a
// backup connection.
if ((mPrimaryStreamStatus == NS_NET_STATUS_CONNECTING_TO) && mEnt &&
if (mPrimaryTransport.ConnectingOrRetry() && mEnt &&
!mBackupTransport.mSocketTransport && !mSynTimer && !mIsHttp3) {
SetupBackupTimer();
}
@ -590,7 +812,43 @@ void DnsAndConnectSocket::CloseTransports(nsresult error) {
}
}
DnsAndConnectSocket::TransportSetup::TransportSetup(bool isBackup)
: mState(TransportSetup::TransportSetupState::INIT), mIsBackup(isBackup) {}
nsresult DnsAndConnectSocket::TransportSetup::Init(
DnsAndConnectSocket* dnsAndSock) {
nsresult rv;
mSynStarted = TimeStamp::Now();
if (mSkipDnsResolution) {
mState = TransportSetup::TransportSetupState::CONNECTING;
rv = SetupStreams(dnsAndSock);
} else {
mState = TransportSetup::TransportSetupState::RESOLVING;
rv = ResolveHost(dnsAndSock);
}
if (NS_FAILED(rv)) {
CloseAll();
mState = TransportSetup::TransportSetupState::DONE;
}
return rv;
}
void DnsAndConnectSocket::TransportSetup::CancelDnsResolution() {
if (mDNSRequest) {
mDNSRequest->Cancel(NS_ERROR_ABORT);
mDNSRequest = nullptr;
}
if (mState == TransportSetup::TransportSetupState::RESOLVING) {
mState = TransportSetup::TransportSetupState::INIT;
}
}
void DnsAndConnectSocket::TransportSetup::Abandon() {
CloseAll();
mState = TransportSetup::TransportSetupState::DONE;
}
void DnsAndConnectSocket::TransportSetup::CloseAll() {
// Tell socket (and backup socket) to forget the half open socket.
if (mSocketTransport) {
mSocketTransport->SetEventSink(nullptr, nullptr);
@ -610,10 +868,71 @@ void DnsAndConnectSocket::TransportSetup::Abandon() {
mStreamIn->AsyncWait(nullptr, 0, 0, nullptr);
mStreamIn = nullptr;
}
if (mDNSRequest) {
mDNSRequest->Cancel(NS_ERROR_ABORT);
mDNSRequest = nullptr;
}
mConnectedOK = false;
}
nsresult DnsAndConnectSocket::TransportSetup::CheckConnectedResult(
DnsAndConnectSocket* dnsAndSock) {
mState = TransportSetup::TransportSetupState::CONNECTING_DONE;
if (mSkipDnsResolution) {
return NS_OK;
}
bool retryDns = false;
mSocketTransport->GetRetryDnsIfPossible(&retryDns);
if (!retryDns) {
return NS_OK;
}
bool retry = false;
if (mRetryWithDifferentIPFamily) {
mRetryWithDifferentIPFamily = false;
mDnsFlags ^= (nsIDNSService::RESOLVE_DISABLE_IPV6 |
nsIDNSService::RESOLVE_DISABLE_IPV4);
mResetFamilyPreference = true;
retry = true;
} else if (!(mDnsFlags & nsIDNSService::RESOLVE_DISABLE_TRR)) {
bool trrEnabled;
mDNSRecord->IsTRR(&trrEnabled);
if (trrEnabled) {
uint32_t trrMode = 0;
mDNSRecord->GetEffectiveTRRMode(&trrMode);
// If current trr mode is trr only, we should not retry.
if (trrMode != 3) {
// Drop state to closed. This will trigger a new round of
// DNS resolving. Bypass the cache this time since the
// cached data came from TRR and failed already!
LOG((" failed to connect with TRR enabled, try w/o\n"));
mDnsFlags |= nsIDNSService::RESOLVE_DISABLE_TRR |
nsIDNSService::RESOLVE_BYPASS_CACHE |
nsIDNSService::RESOLVE_REFRESH_CACHE;
retry = true;
}
}
}
if (retry) {
CloseAll();
mState = TransportSetup::TransportSetupState::RESOLVING;
nsresult rv = ResolveHost(dnsAndSock);
if (NS_FAILED(rv)) {
CloseAll();
mState = TransportSetup::TransportSetupState::DONE;
}
return rv;
}
return NS_OK;
}
nsresult DnsAndConnectSocket::TransportSetup::SetupConn(
nsAHttpTransaction* transaction, ConnectionEntry* ent,
nsAHttpTransaction* transaction, ConnectionEntry* ent, nsresult status,
HttpConnectionBase** connection) {
RefPtr<HttpConnectionBase> conn;
if (!ent->mConnInfo->IsHttp3()) {
@ -640,29 +959,34 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupConn(
transaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
nsresult rv = conn->Init(
ent->mConnInfo, gHttpHandler->ConnMgr()->mMaxRequestDelay,
mSocketTransport, mStreamIn, mStreamOut, mConnectedOK, callbacks,
mSocketTransport, mStreamIn, mStreamOut, mConnectedOK, status, callbacks,
PR_MillisecondsToInterval(static_cast<uint32_t>(
(TimeStamp::Now() - mSynStarted).ToMilliseconds())));
bool resetPreference = false;
mSocketTransport->GetResetIPFamilyPreference(&resetPreference);
if (resetPreference) {
if (mResetFamilyPreference ||
(mSocketTransport &&
NS_SUCCEEDED(
mSocketTransport->GetResetIPFamilyPreference(&resetPreference)) &&
resetPreference)) {
ent->ResetIPFamilyPreference();
}
NetAddr peeraddr;
if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) {
if (mSocketTransport &&
NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) {
ent->RecordIPFamilyPreference(peeraddr.raw.family);
}
conn.forget(connection);
mSocketTransport = nullptr;
mStreamOut = nullptr;
mStreamIn = nullptr;
mState = TransportSetup::TransportSetupState::DONE;
return rv;
}
nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
DnsAndConnectSocket *dnsAndSock, bool isBackup) {
DnsAndConnectSocket* dnsAndSock) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(dnsAndSock->mEnt);
@ -700,7 +1024,8 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
if (routedSTS) {
rv = routedSTS->CreateRoutedTransport(
socketTypes, ci->GetOrigin(), ci->OriginPort(), ci->GetRoutedHost(),
ci->RoutedPort(), ci->ProxyInfo(), getter_AddRefs(socketTransport));
ci->RoutedPort(), ci->ProxyInfo(), mDNSRecord,
getter_AddRefs(socketTransport));
} else {
if (!ci->GetRoutedHost().IsEmpty()) {
// There is a route requested, but the legacy nsISocketTransportService
@ -714,7 +1039,8 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
}
rv = sts->CreateTransport(socketTypes, ci->GetOrigin(), ci->OriginPort(),
ci->ProxyInfo(), getter_AddRefs(socketTransport));
ci->ProxyInfo(), mDNSRecord,
getter_AddRefs(socketTransport));
}
NS_ENSURE_SUCCESS(rv, rv);
@ -750,15 +1076,13 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
if (ci->HasIPHintAddress()) {
nsCOMPtr<nsIDNSService> dns =
do_GetService("@mozilla.org/network/dns-service;1", &rv);
if (NS_FAILED(rv)) {
return rv;
}
NS_ENSURE_SUCCESS(rv, rv);
// The spec says: "If A and AAAA records for TargetName are locally
// available, the client SHOULD ignore these hints.", so we check if the DNS
// record is in cache before setting USE_IP_HINT_ADDRESS.
nsCOMPtr<nsIDNSRecord> record;
rv = dns->ResolveNative(ci->GetRoutedHost(), nsIDNSService::RESOLVE_OFFLINE,
rv = dns->ResolveNative(mHost, nsIDNSService::RESOLVE_OFFLINE,
dnsAndSock->mEnt->mConnInfo->GetOriginAttributes(),
getter_AddRefs(record));
if (NS_FAILED(rv) || !record) {
@ -785,12 +1109,12 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
// From the same reason, let the backup socket fail faster to try the other
// family.
uint16_t fallbackTimeout =
isBackup ? gHttpHandler->GetFallbackSynTimeout() : 0;
mIsBackup ? gHttpHandler->GetFallbackSynTimeout() : 0;
if (fallbackTimeout) {
socketTransport->SetTimeout(nsISocketTransport::TIMEOUT_CONNECT,
fallbackTimeout);
}
} else if (isBackup && gHttpHandler->FastFallbackToIPv4()) {
} else if (mIsBackup && gHttpHandler->FastFallbackToIPv4()) {
// For backup connections, we disable IPv6. That's because some users have
// broken IPv6 connectivity (leading to very long timeouts), and disabling
// IPv6 on the backup connection gives them a much better user experience
@ -842,7 +1166,6 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
getter_AddRefs(sin));
NS_ENSURE_SUCCESS(rv, rv);
mSynStarted = TimeStamp::Now();
mSocketTransport = socketTransport.forget();
mStreamIn = do_QueryInterface(sin);
mStreamOut = do_QueryInterface(sout);
@ -855,5 +1178,88 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
return rv;
}
nsresult DnsAndConnectSocket::TransportSetup::ResolveHost(
DnsAndConnectSocket* dnsAndSock) {
LOG(("DnsAndConnectSocket::TransportSetup::ResolveHost [this=%p %s%s]", this,
PromiseFlatCString(mHost).get(),
(mDnsFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) ? " bypass cache"
: ""));
nsCOMPtr<nsIDNSService> dns = nullptr;
auto initTask = [&dns]() { dns = do_GetService(NS_DNSSERVICE_CID); };
if (!NS_IsMainThread()) {
// Forward to the main thread synchronously.
RefPtr<nsIThread> mainThread = do_GetMainThread();
if (!mainThread) {
return NS_ERROR_FAILURE;
}
SyncRunnable::DispatchToThread(
mainThread,
new SyncRunnable(NS_NewRunnableFunction(
"nsSocketTransport::ResolveHost->GetDNSService", initTask)));
} else {
initTask();
}
if (!dns) {
return NS_ERROR_FAILURE;
}
if (!mIsBackup) {
dnsAndSock->mTransaction->OnTransportStatus(
nullptr, NS_NET_STATUS_RESOLVING_HOST, 0);
}
return dns->AsyncResolveNative(
mHost, nsIDNSService::RESOLVE_TYPE_DEFAULT, mDnsFlags, nullptr,
dnsAndSock, gSocketTransportService,
dnsAndSock->mEnt->mConnInfo->GetOriginAttributes(),
getter_AddRefs(mDNSRequest));
}
nsresult DnsAndConnectSocket::TransportSetup::OnLookupComplete(
DnsAndConnectSocket* dnsAndSock, nsIDNSRecord* rec, nsresult status) {
mDNSRequest = nullptr;
if (NS_SUCCEEDED(status)) {
mDNSRecord = do_QueryInterface(rec);
MOZ_ASSERT(mDNSRecord);
nsresult rv = SetupStreams(dnsAndSock);
if (NS_SUCCEEDED(rv)) {
mState = TransportSetup::TransportSetupState::CONNECTING;
} else {
CloseAll();
mState = TransportSetup::TransportSetupState::DONE;
}
return rv;
}
// DNS lookup status failed
bool retry = false;
if (mDnsFlags & nsIDNSService::RESOLVE_IP_HINT) {
mDnsFlags &= ~nsIDNSService::RESOLVE_IP_HINT;
retry = true;
} else if ((status == NS_ERROR_UNKNOWN_HOST) && mRetryWithDifferentIPFamily) {
mRetryWithDifferentIPFamily = false;
mDnsFlags ^= (nsIDNSService::RESOLVE_DISABLE_IPV6 |
nsIDNSService::RESOLVE_DISABLE_IPV4);
mResetFamilyPreference = true;
retry = true;
}
if (retry) {
mState = TransportSetup::TransportSetupState::RETRY_RESOLVING;
nsresult rv = ResolveHost(dnsAndSock);
if (NS_FAILED(rv)) {
CloseAll();
mState = TransportSetup::TransportSetupState::DONE;
}
return rv;
}
mState = TransportSetup::TransportSetupState::DONE;
return status;
}
} // namespace net
} // namespace mozilla

View File

@ -12,6 +12,9 @@
#include "nsHttpConnection.h"
#include "nsHttpTransaction.h"
#include "nsIAsyncOutputStream.h"
#include "nsICancelable.h"
#include "nsIDNSListener.h"
#include "nsIDNSRecord.h"
#include "nsINamed.h"
#include "nsITransport.h"
#include "nsWeakReference.h"
@ -35,7 +38,8 @@ class DnsAndConnectSocket final : public nsIOutputStreamCallback,
public nsIInterfaceRequestor,
public nsITimerCallback,
public nsINamed,
public nsSupportsWeakReference {
public nsSupportsWeakReference,
public nsIDNSListener {
~DnsAndConnectSocket();
public:
@ -46,12 +50,13 @@ class DnsAndConnectSocket final : public nsIOutputStreamCallback,
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
NS_DECL_NSIDNSLISTENER
DnsAndConnectSocket(ConnectionEntry* ent, nsAHttpTransaction* trans,
uint32_t caps, bool speculative, bool isFromPredictor,
bool urgentStart);
[[nodiscard]] nsresult SetupPrimaryStreams();
[[nodiscard]] nsresult Init();
void Abandon();
double Duration(TimeStamp epoch);
void CloseTransports(nsresult error);
@ -75,29 +80,110 @@ class DnsAndConnectSocket final : public nsIOutputStreamCallback,
void Unclaim();
private:
/**
* State:
* INIT: initial state. From this state:
* 1) change the state to RESOLVING and start the primary DNS lookup
* if mSkipDnsResolution is false,
* 2) or the lookup is skip and the state changes to CONNECTING and
* start the backup timer.
* 3) or changes to DONE in case of an error.
* RESOLVING: the primary DNS resolution is in progress. From this state
* we transition into CONNECTING or DONE.
* CONNECTING: We change to this state when the primary connection has
* started. At that point the backup timer is started.
* ONE_CONNECTED: We change into this state when one of the connections
* is connected and the second is in progres.
* DONE
*
* Events:
* INIT_EVENT: Start the primary dns resolution (if mSkipDnsResolution is
* false), otherwise start the primary connection.
* RESOLVED_PRIMARY_EVENT: the primary DNS resolution is done. This event
* may be resent due to DNS retries
* CONNECTED_EVENT: A connecion (primary or backup) is done
*/
enum DnsAndSocketState {
INIT,
RESOLVING,
CONNECTING,
ONE_CONNECTED,
DONE
} mState = INIT;
enum SetupEvents {
INIT_EVENT,
RESOLVED_PRIMARY_EVENT,
PRIMARY_DONE_EVENT,
BACKUP_DONE_EVENT,
BACKUP_TIMER_FIRED_EVENT
};
// This structure is responsible for performing DNS lookup, creating socket
// and connecting the socket.
struct TransportSetup {
enum TransportSetupState {
INIT,
RESOLVING,
RETRY_RESOLVING,
CONNECTING,
CONNECTING_DONE,
DONE
} mState;
bool FirstResolving() {
return mState == TransportSetup::TransportSetupState::RESOLVING;
}
bool ConnectingOrRetry() {
return (mState == TransportSetup::TransportSetupState::CONNECTING) ||
(mState == TransportSetup::TransportSetupState::RETRY_RESOLVING) ||
(mState == TransportSetup::TransportSetupState::CONNECTING_DONE);
}
bool DoneConnecting() {
return (mState == TransportSetup::TransportSetupState::CONNECTING_DONE) ||
(mState == TransportSetup::TransportSetupState::DONE);
}
nsCString mHost;
nsCOMPtr<nsICancelable> mDNSRequest;
nsCOMPtr<nsIDNSAddrRecord> mDNSRecord;
uint32_t mDnsFlags = 0;
bool mRetryWithDifferentIPFamily = false;
bool mResetFamilyPreference = false;
bool mSkipDnsResolution = false;
nsCOMPtr<nsISocketTransport> mSocketTransport;
nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
nsCOMPtr<nsIAsyncInputStream> mStreamIn;
TimeStamp mSynStarted;
bool mConnectedOK = false;
bool mIsBackup;
explicit TransportSetup(bool isBackup);
nsresult Init(DnsAndConnectSocket* dnsAndSock);
void CancelDnsResolution();
void Abandon();
void CloseAll();
nsresult SetupConn(nsAHttpTransaction* transaction, ConnectionEntry* ent,
HttpConnectionBase** connection);
[[nodiscard]] nsresult SetupStreams(DnsAndConnectSocket* dnsAndSock,
bool isBackup);
nsresult status, HttpConnectionBase** connection);
[[nodiscard]] nsresult SetupStreams(DnsAndConnectSocket* dnsAndSock);
nsresult ResolveHost(DnsAndConnectSocket* dnsAndSock);
nsresult OnLookupComplete(DnsAndConnectSocket* dnsAndSock,
nsIDNSRecord* rec, nsresult status);
nsresult CheckConnectedResult(DnsAndConnectSocket* dnsAndSock);
};
[[nodiscard]] nsresult SetupBackupStreams();
[[nodiscard]] nsresult SetupStreams(bool isBackup);
nsresult SetupConn(nsIAsyncOutputStream* out);
nsresult SetupConn(bool isPrimary, nsresult status);
void SetupBackupTimer();
void CancelBackupTimer();
bool IsPrimary(nsITransport* trans);
bool IsPrimary(nsIAsyncOutputStream* out);
bool IsPrimary(nsICancelable* dnsRequest);
bool IsBackup(nsITransport* trans);
bool IsBackup(nsIAsyncOutputStream* out);
bool IsBackup(nsICancelable* dnsRequest);
// To find out whether |mTransaction| is still in the connection entry's
// pending queue. If the transaction is found and |removeWhenFound| is
@ -105,6 +191,10 @@ class DnsAndConnectSocket final : public nsIOutputStreamCallback,
already_AddRefed<PendingTransactionInfo> FindTransactionHelper(
bool removeWhenFound);
void CheckProxyConfig();
nsresult SetupDnsFlags();
nsresult SetupEvent(SetupEvents event);
RefPtr<nsAHttpTransaction> mTransaction;
bool mDispatchedMTransaction = false;
@ -142,13 +232,16 @@ class DnsAndConnectSocket final : public nsIOutputStreamCallback,
// case we can free this transaction to be claimed by other
// transactions.
bool mFreeToUse = true;
nsresult mPrimaryStreamStatus = NS_OK;
RefPtr<ConnectionEntry> mEnt;
nsCOMPtr<nsITimer> mSynTimer;
TransportSetup mBackupTransport;
bool mIsHttp3;
bool mSkipDnsResolution = false;
bool mProxyNotTransparent = false;
bool mProxyTransparentResolvesHost = false;
};
NS_DEFINE_STATIC_IID_ACCESSOR(DnsAndConnectSocket, NS_DNSANDCONNECTSOCKET_IID)

View File

@ -61,7 +61,7 @@ class HttpConnectionBase : public nsSupportsWeakReference {
[[nodiscard]] virtual nsresult Init(
nsHttpConnectionInfo* info, uint16_t maxHangTime, nsISocketTransport*,
nsIAsyncInputStream*, nsIAsyncOutputStream*, bool connectedTransport,
nsIInterfaceRequestor*, PRIntervalTime) = 0;
nsresult status, nsIInterfaceRequestor*, PRIntervalTime) = 0;
// Activate causes the given transaction to be processed on this
// connection. It fails if there is already an existing transaction unless
@ -161,6 +161,7 @@ class HttpConnectionBase : public nsSupportsWeakReference {
nsTArray<HttpTrafficCategory> mTrafficCategory;
PRIntervalTime mRtt;
nsresult mErrorBeforeConnect = NS_OK;
};
NS_DEFINE_STATIC_IID_ACCESSOR(HttpConnectionBase, HTTPCONNECTIONBASE_IID)
@ -168,7 +169,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(HttpConnectionBase, HTTPCONNECTIONBASE_IID)
#define NS_DECL_HTTPCONNECTIONBASE \
[[nodiscard]] nsresult Init( \
nsHttpConnectionInfo*, uint16_t, nsISocketTransport*, \
nsIAsyncInputStream*, nsIAsyncOutputStream*, bool, \
nsIAsyncInputStream*, nsIAsyncOutputStream*, bool, nsresult, \
nsIInterfaceRequestor*, PRIntervalTime) override; \
[[nodiscard]] nsresult Activate(nsAHttpTransaction*, uint32_t, int32_t) \
override; \

View File

@ -90,11 +90,12 @@ HttpConnectionUDP::~HttpConnectionUDP() {
nsresult HttpConnectionUDP::Init(
nsHttpConnectionInfo* info, uint16_t maxHangTime,
nsISocketTransport* transport, nsIAsyncInputStream* instream,
nsIAsyncOutputStream* outstream, bool connectedTransport,
nsIAsyncOutputStream* outstream, bool connectedTransport, nsresult status,
nsIInterfaceRequestor* callbacks, PRIntervalTime rtt) {
LOG1(("HttpConnectionUDP::Init this=%p sockettransport=%p", this, transport));
NS_ENSURE_ARG_POINTER(info);
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
MOZ_ASSERT(NS_SUCCEEDED(status) || !connectedTransport);
mConnectedTransport = connectedTransport;
mConnInfo = info;
@ -103,29 +104,38 @@ nsresult HttpConnectionUDP::Init(
mLastWriteTime = mLastReadTime = PR_IntervalNow();
mRtt = rtt;
mSocketTransport = transport;
mSocketIn = instream;
mSocketOut = outstream;
mErrorBeforeConnect = status;
if (NS_FAILED(mErrorBeforeConnect)) {
MOZ_ASSERT(!mConnectedTransport);
// See explanation for non-strictness of this operation in
// SetSecurityCallbacks.
mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
"HttpConnectionUDP::mCallbacks", callbacks, false);
} else {
mSocketTransport = transport;
mSocketIn = instream;
mSocketOut = outstream;
MOZ_ASSERT(mConnInfo->IsHttp3());
mHttp3Session = new Http3Session();
nsresult rv = mHttp3Session->Init(mConnInfo, mSocketTransport, this);
if (NS_FAILED(rv)) {
LOG(
("HttpConnectionUDP::Init mHttp3Session->Init failed "
"[this=%p rv=%x]\n",
this, static_cast<uint32_t>(rv)));
return rv;
MOZ_ASSERT(mConnInfo->IsHttp3());
mHttp3Session = new Http3Session();
nsresult rv = mHttp3Session->Init(mConnInfo, mSocketTransport, this);
if (NS_FAILED(rv)) {
LOG(
("HttpConnectionUDP::Init mHttp3Session->Init failed "
"[this=%p rv=%x]\n",
this, static_cast<uint32_t>(rv)));
return rv;
}
// See explanation for non-strictness of this operation in
// SetSecurityCallbacks.
mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
"HttpConnectionUDP::mCallbacks", callbacks, false);
mSocketTransport->SetEventSink(this, nullptr);
mSocketTransport->SetSecurityCallbacks(this);
}
// See explanation for non-strictness of this operation in
// SetSecurityCallbacks.
mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
"HttpConnectionUDP::mCallbacks", callbacks, false);
mSocketTransport->SetEventSink(this, nullptr);
mSocketTransport->SetSecurityCallbacks(this);
return NS_OK;
}
@ -140,7 +150,7 @@ nsresult HttpConnectionUDP::Activate(nsAHttpTransaction* trans, uint32_t caps,
// For QUIC we have HttpConnecitonUDP before the actual connection
// has been establish so wait for TLS handshake to be finished before
// we mark the connection 'experienced'.
if (!mExperienced && mHttp3Session->IsConnected()) {
if (!mExperienced && mHttp3Session && mHttp3Session->IsConnected()) {
mExperienced = true;
}
if (mBootstrappedTimingsSet) {
@ -164,6 +174,12 @@ nsresult HttpConnectionUDP::Activate(nsAHttpTransaction* trans, uint32_t caps,
// Connection failures are Activated() just like regular transacions.
// If we don't have a confirmation of a connected socket then test it
// with a write() to get relevant error code.
if (NS_FAILED(mErrorBeforeConnect)) {
CloseTransaction(nullptr, mErrorBeforeConnect);
trans->Close(mErrorBeforeConnect);
gHttpHandler->ExcludeHttp3(mConnInfo);
return mErrorBeforeConnect;
}
if (!mConnectedTransport) {
uint32_t count;
nsresult rv = NS_OK;
@ -175,6 +191,7 @@ nsresult HttpConnectionUDP::Activate(nsAHttpTransaction* trans, uint32_t caps,
this, static_cast<uint32_t>(rv)));
mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
CloseTransaction(mHttp3Session, rv);
gHttpHandler->ExcludeHttp3(mConnInfo);
trans->Close(rv);
return rv;
}

View File

@ -1182,7 +1182,7 @@ bool SpdyConnectTransaction::MapStreamToHttpConnection(
TimeDuration rtt = TimeStamp::Now() - mTimestampSyn;
DebugOnly<nsresult> rv = mTunneledConn->Init(
aConnInfo, gHttpHandler->ConnMgr()->MaxRequestDelay(), mTunnelTransport,
mTunnelStreamIn, mTunnelStreamOut, true, callbacks,
mTunnelStreamIn, mTunnelStreamOut, true, NS_OK, callbacks,
PR_MillisecondsToInterval(static_cast<uint32_t>(rtt.ToMilliseconds())));
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (mForcePlainText) {
@ -2147,6 +2147,11 @@ SocketTransportShim::SetQoSBits(uint8_t aQoSBits) {
return mWrapped->SetQoSBits(aQoSBits);
}
NS_IMETHODIMP
SocketTransportShim::GetRetryDnsIfPossible(bool* aRetry) {
return mWrapped->GetRetryDnsIfPossible(aRetry);
}
NS_IMPL_ISUPPORTS(TLSFilterTransaction, nsITimerCallback, nsINamed)
NS_IMPL_ISUPPORTS(SocketTransportShim, nsISocketTransport, nsITransport)
NS_IMPL_ISUPPORTS(InputStreamShim, nsIInputStream, nsIAsyncInputStream)

View File

@ -169,11 +169,12 @@ nsHttpConnection::~nsHttpConnection() {
nsresult nsHttpConnection::Init(
nsHttpConnectionInfo* info, uint16_t maxHangTime,
nsISocketTransport* transport, nsIAsyncInputStream* instream,
nsIAsyncOutputStream* outstream, bool connectedTransport,
nsIAsyncOutputStream* outstream, bool connectedTransport, nsresult status,
nsIInterfaceRequestor* callbacks, PRIntervalTime rtt) {
LOG1(("nsHttpConnection::Init this=%p sockettransport=%p", this, transport));
NS_ENSURE_ARG_POINTER(info);
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
MOZ_ASSERT(NS_SUCCEEDED(status) || !connectedTransport);
mConnectedTransport = connectedTransport;
mConnInfo = info;
@ -192,8 +193,11 @@ nsresult nsHttpConnection::Init(
mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
"nsHttpConnection::mCallbacks", callbacks, false);
mSocketTransport->SetEventSink(this, nullptr);
mSocketTransport->SetSecurityCallbacks(this);
mErrorBeforeConnect = status;
if (NS_SUCCEEDED(mErrorBeforeConnect)) {
mSocketTransport->SetEventSink(this, nullptr);
mSocketTransport->SetSecurityCallbacks(this);
}
return NS_OK;
}
@ -747,15 +751,18 @@ nsresult nsHttpConnection::Activate(nsAHttpTransaction* trans, uint32_t caps,
// Connection failures are Activated() just like regular transacions.
// If we don't have a confirmation of a connected socket then test it
// with a write() to get relevant error code.
if (NS_FAILED(mErrorBeforeConnect)) {
mSocketOutCondition = mErrorBeforeConnect;
mTransaction = trans;
CloseTransaction(mTransaction, mSocketOutCondition);
return mSocketOutCondition;
}
if (!mConnectedTransport) {
if (NS_FAILED(mErrorBeforeConnect)) {
mSocketOutCondition = mErrorBeforeConnect;
} else {
uint32_t count;
mSocketOutCondition = NS_ERROR_FAILURE;
if (mSocketOut) {
mSocketOutCondition = mSocketOut->Write("", 0, &count);
}
uint32_t count;
mSocketOutCondition = NS_ERROR_FAILURE;
if (mSocketOut) {
mSocketOutCondition = mSocketOut->Write("", 0, &count);
}
if (NS_FAILED(mSocketOutCondition) &&
mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {

View File

@ -1504,7 +1504,6 @@ nsresult nsHttpConnectionMgr::DispatchTransaction(ConnectionEntry* ent,
"Connection host = %s\n",
trans->ConnectionInfo()->Origin(), conn->ConnectionInfo()->Origin()));
rv = conn->Activate(trans, caps, priority);
MOZ_ASSERT(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
if (conn->UsingSpdy()) {
AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_SPDY,
@ -1732,7 +1731,7 @@ nsresult nsHttpConnectionMgr::CreateTransport(
// The socket stream holds the reference to the half open
// socket - so if the stream fails to init the half open
// will go away.
nsresult rv = sock->SetupPrimaryStreams();
nsresult rv = sock->Init();
NS_ENSURE_SUCCESS(rv, rv);
if (pendingTransInfo) {

View File

@ -131,7 +131,7 @@ TEST(TestBind, MainTest)
for (int32_t tried = 0; tried < 100; tried++) {
nsCOMPtr<nsISocketTransport> client;
rv = sts->CreateTransport(nsTArray<nsCString>(), "127.0.0.1"_ns, serverPort,
nullptr, getter_AddRefs(client));
nullptr, nullptr, getter_AddRefs(client));
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Bind to a port. It's possible that we are binding to a port that is

View File

@ -443,7 +443,13 @@ function runRawTests(testArray, done, beforeTestCallback) {
var rawTest = testArray[testIndex];
var transport = sts.createTransport([], rawTest.host, rawTest.port, null);
var transport = sts.createTransport(
[],
rawTest.host,
rawTest.port,
null,
null
);
var inStream = transport.openInputStream(0, 0, 0);
var outStream = transport.openOutputStream(0, 0, 0);

View File

@ -59,7 +59,7 @@ async function launchConnection(
-1,
null
);
let trans = sts.createTransport([], dest_host, dest_port, pi);
let trans = sts.createTransport([], dest_host, dest_port, pi, null);
let input = trans.openInputStream(0, 0, 0);
let output = trans.openOutputStream(0, 0, 0);
input = await waitForStream(input, Ci.nsIAsyncInputStream);

View File

@ -66,6 +66,7 @@ add_test(function test_sockets() {
[],
"127.0.0.1",
gServerSocket.port,
null,
null
);
let listener = {

View File

@ -182,6 +182,7 @@ function testIpv4() {
[],
"127.0.0.1",
serv.port,
null,
null
);
/*

View File

@ -187,7 +187,7 @@ function test_hostnames_resolving_to_addresses(host, next) {
Ci.nsISocketTransportService
);
Assert.notEqual(typeof sts, undefined);
var transport = sts.createTransport([], host, 80, null);
var transport = sts.createTransport([], host, 80, null, null);
Assert.notEqual(typeof transport, undefined);
transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918;
@ -269,7 +269,7 @@ function test_proxies(proxyHost, next) {
var proxyInfo = pps.newProxyInfo("http", proxyHost, 8080, "", "", 0, 1, null);
Assert.notEqual(typeof proxyInfo, undefined);
var transport = sts.createTransport([], "dummyHost", 80, proxyInfo);
var transport = sts.createTransport([], "dummyHost", 80, proxyInfo, null);
Assert.notEqual(typeof transport, undefined);
transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918;

View File

@ -135,6 +135,7 @@ function startClient(port, cert, expectingAlert, tlsVersion) {
["ssl"],
"127.0.0.1",
port,
null,
null
);
let input;

View File

@ -96,6 +96,7 @@ function startClient(port) {
["ssl"],
"127.0.0.1",
port,
null,
null
);
let input;

View File

@ -523,7 +523,13 @@ async function asyncConnectTo(
let sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService(
Ci.nsISocketTransportService
);
this.transport = sts.createTransport(["ssl"], host, REMOTE_PORT, null);
this.transport = sts.createTransport(
["ssl"],
host,
REMOTE_PORT,
null,
null
);
if (aEchConfig) {
this.transport.setEchConfig(atob(aEchConfig));
}

View File

@ -390,7 +390,7 @@ class SocksTestClient {
null
);
this.trans = sts.createTransport([], dest.host, dest.port, pi);
this.trans = sts.createTransport([], dest.host, dest.port, pi, null);
this.input = this.trans.openInputStream(
Ci.nsITransport.OPEN_BLOCKING,
0,