Bug 1816677 - Allow to retry diffrent IP family for Http/3, r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D180679
This commit is contained in:
@@ -12510,6 +12510,19 @@
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# When a Http/3 connection failed, whether to retry with a different IP address.
|
||||
- name: network.http.http3.retry_different_ip_family
|
||||
type: RelaxedAtomicBool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# This is for testing purpose. When true, nsUDPSocket::SendWithAddress will
|
||||
# return NS_ERROR_CONNECTION_REFUSED for address "::1".
|
||||
- name: network.http.http3.block_loopback_ipv6_addr
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# When true, a http request will be upgraded to https when HTTPS RR is
|
||||
# available.
|
||||
- name: network.dns.upgrade_with_https_rr
|
||||
|
||||
@@ -1147,6 +1147,11 @@ nsUDPSocket::SendWithAddress(const NetAddr* aAddr,
|
||||
NS_ENSURE_ARG(aAddr);
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
|
||||
if (StaticPrefs::network_http_http3_block_loopback_ipv6_addr() &&
|
||||
aAddr->raw.family == AF_INET6 && aAddr->IsLoopbackAddr()) {
|
||||
return NS_ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
|
||||
*_retval = 0;
|
||||
|
||||
PRNetAddr prAddr;
|
||||
|
||||
@@ -1004,5 +1004,55 @@ nsresult ConnectionEntry::CreateDnsAndConnectSocket(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool ConnectionEntry::AllowToRetryDifferentIPFamilyForHttp3(nsresult aError) {
|
||||
LOG(
|
||||
("ConnectionEntry::AllowToRetryDifferentIPFamilyForHttp3 %p "
|
||||
"error=%" PRIx32,
|
||||
this, static_cast<uint32_t>(aError)));
|
||||
if (!IsHttp3()) {
|
||||
MOZ_ASSERT(false, "Should not be called for non Http/3 connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!StaticPrefs::network_http_http3_retry_different_ip_family()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only allow to retry with these two errors.
|
||||
if (aError != NS_ERROR_CONNECTION_REFUSED &&
|
||||
aError != NS_ERROR_PROXY_CONNECTION_REFUSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Already retried once.
|
||||
if (mRetriedDifferentIPFamilyForHttp3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionEntry::SetRetryDifferentIPFamilyForHttp3(uint16_t aIPFamily) {
|
||||
LOG(("ConnectionEntry::SetRetryDifferentIPFamilyForHttp3 %p, af=%u", this,
|
||||
aIPFamily));
|
||||
|
||||
mPreferIPv4 = false;
|
||||
mPreferIPv6 = false;
|
||||
|
||||
if (aIPFamily == AF_INET) {
|
||||
mPreferIPv6 = true;
|
||||
}
|
||||
|
||||
if (aIPFamily == AF_INET6) {
|
||||
mPreferIPv4 = true;
|
||||
}
|
||||
|
||||
mRetriedDifferentIPFamilyForHttp3 = true;
|
||||
|
||||
LOG((" %p prefer ipv4=%d, ipv6=%d", this, (bool)mPreferIPv4,
|
||||
(bool)mPreferIPv6));
|
||||
MOZ_DIAGNOSTIC_ASSERT(mPreferIPv4 ^ mPreferIPv6);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -195,6 +195,9 @@ class ConnectionEntry {
|
||||
|
||||
void MaybeUpdateEchConfig(nsHttpConnectionInfo* aConnInfo);
|
||||
|
||||
bool AllowToRetryDifferentIPFamilyForHttp3(nsresult aError);
|
||||
void SetRetryDifferentIPFamilyForHttp3(uint16_t aIPFamily);
|
||||
|
||||
private:
|
||||
void InsertIntoIdleConnections_internal(nsHttpConnection* conn);
|
||||
void RemoveFromIdleConnectionsIndex(size_t inx);
|
||||
@@ -211,6 +214,8 @@ class ConnectionEntry {
|
||||
|
||||
PendingTransactionQueue mPendingQ;
|
||||
~ConnectionEntry();
|
||||
|
||||
bool mRetriedDifferentIPFamilyForHttp3 = false;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
||||
@@ -417,8 +417,10 @@ DnsAndConnectSocket::GetName(nsACString& aName) {
|
||||
NS_IMETHODIMP
|
||||
DnsAndConnectSocket::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
|
||||
nsresult status) {
|
||||
LOG(("DnsAndConnectSocket::OnLookupComplete: this=%p status %" PRIx32 ".",
|
||||
this, static_cast<uint32_t>(status)));
|
||||
LOG((
|
||||
"DnsAndConnectSocket::OnLookupComplete: this=%p mState=%d status %" PRIx32
|
||||
".",
|
||||
this, mState, static_cast<uint32_t>(status)));
|
||||
|
||||
if (nsCOMPtr<nsIDNSAddrRecord> addrRecord = do_QueryInterface((rec))) {
|
||||
nsIRequest::TRRMode effectivemode = nsIRequest::TRR_DEFAULT_MODE;
|
||||
@@ -465,6 +467,13 @@ DnsAndConnectSocket::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || mIsHttp3) {
|
||||
// If we are retrying DNS, we should not setup the connection.
|
||||
if (mIsHttp3 && mPrimaryTransport.mState ==
|
||||
TransportSetup::TransportSetupState::RETRY_RESOLVING) {
|
||||
LOG(("Retry DNS for Http3"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Before calling SetupConn we need to hold reference to this, i.e. a
|
||||
// delete protector, because the corresponding ConnectionEntry may be
|
||||
// abandoned and that will abandon this DnsAndConnectSocket.
|
||||
|
||||
@@ -230,11 +230,18 @@ void Http3Session::Shutdown() {
|
||||
|
||||
bool isEchRetry = mError == mozilla::psm::GetXPCOMFromNSSError(
|
||||
SSL_ERROR_ECH_RETRY_WITH_ECH);
|
||||
bool allowToRetryWithDifferentIPFamily =
|
||||
mBeforeConnectedError &&
|
||||
gHttpHandler->ConnMgr()->AllowToRetryDifferentIPFamilyForHttp3(mConnInfo,
|
||||
mError);
|
||||
LOG(("Http3Session::Shutdown %p allowToRetryWithDifferentIPFamily=%d", this,
|
||||
allowToRetryWithDifferentIPFamily));
|
||||
if ((mBeforeConnectedError ||
|
||||
(mError == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR)) &&
|
||||
(mError !=
|
||||
mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) &&
|
||||
!isEchRetry && !mConnInfo->GetWebTransport()) {
|
||||
!isEchRetry && !mConnInfo->GetWebTransport() &&
|
||||
!allowToRetryWithDifferentIPFamily && !mDontExclude) {
|
||||
gHttpHandler->ExcludeHttp3(mConnInfo);
|
||||
}
|
||||
|
||||
@@ -248,9 +255,30 @@ void Http3Session::Shutdown() {
|
||||
// We have to propagate this error to nsHttpTransaction, so the
|
||||
// transaction will be restarted with a new echConfig.
|
||||
stream->Close(mError);
|
||||
} else {
|
||||
if (allowToRetryWithDifferentIPFamily && mNetAddr) {
|
||||
NetAddr addr;
|
||||
mNetAddr->GetNetAddr(&addr);
|
||||
gHttpHandler->ConnMgr()->SetRetryDifferentIPFamilyForHttp3(
|
||||
mConnInfo, addr.raw.family);
|
||||
nsHttpTransaction* trans =
|
||||
stream->Transaction()->QueryHttpTransaction();
|
||||
if (trans) {
|
||||
// This is a bit hacky. We redispatch the transaction here to avoid
|
||||
// touching the complicated retry logic in nsHttpTransaction.
|
||||
trans->RemoveConnection();
|
||||
Unused << gHttpHandler->InitiateTransaction(trans,
|
||||
trans->Priority());
|
||||
} else {
|
||||
stream->Close(NS_ERROR_NET_RESET);
|
||||
}
|
||||
// Since Http3Session::Shutdown can be called multiple times, we set
|
||||
// mDontExclude for not putting this domain into the excluded list.
|
||||
mDontExclude = true;
|
||||
} else {
|
||||
stream->Close(NS_ERROR_NET_RESET);
|
||||
}
|
||||
}
|
||||
} else if (!stream->HasStreamId()) {
|
||||
if (NS_SUCCEEDED(mError)) {
|
||||
// Connection has not been started yet. We can restart it.
|
||||
|
||||
@@ -370,6 +370,8 @@ class Http3Session final : public nsAHttpTransaction, public nsAHttpConnection {
|
||||
nsTArray<RefPtr<Http3StreamBase>> mWebTransportStreams;
|
||||
|
||||
bool mHasWebTransportSession = false;
|
||||
// When true, we don't add this connection info into the Http/3 excluded list.
|
||||
bool mDontExclude = false;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(Http3Session, NS_HTTP3SESSION_IID);
|
||||
|
||||
@@ -5962,9 +5962,15 @@ HttpBaseChannel::CancelByURLClassifier(nsresult aErrorCode) {
|
||||
return Cancel(aErrorCode);
|
||||
}
|
||||
|
||||
void HttpBaseChannel::SetIPv4Disabled() { mCaps |= NS_HTTP_DISABLE_IPV4; }
|
||||
NS_IMETHODIMP HttpBaseChannel::SetIPv4Disabled() {
|
||||
mCaps |= NS_HTTP_DISABLE_IPV4;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void HttpBaseChannel::SetIPv6Disabled() { mCaps |= NS_HTTP_DISABLE_IPV6; }
|
||||
NS_IMETHODIMP HttpBaseChannel::SetIPv6Disabled() {
|
||||
mCaps |= NS_HTTP_DISABLE_IPV6;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP HttpBaseChannel::GetResponseEmbedderPolicy(
|
||||
bool aIsOriginTrialCoepCredentiallessEnabled,
|
||||
|
||||
@@ -331,8 +331,8 @@ class HttpBaseChannel : public nsHashPropertyBag,
|
||||
NS_IMETHOD GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) override;
|
||||
NS_IMETHOD SetNavigationStartTimeStamp(TimeStamp aTimeStamp) override;
|
||||
NS_IMETHOD CancelByURLClassifier(nsresult aErrorCode) override;
|
||||
virtual void SetIPv4Disabled(void) override;
|
||||
virtual void SetIPv6Disabled(void) override;
|
||||
NS_IMETHOD SetIPv4Disabled(void) override;
|
||||
NS_IMETHOD SetIPv6Disabled(void) override;
|
||||
NS_IMETHOD GetCrossOriginOpenerPolicy(
|
||||
nsILoadInfo::CrossOriginOpenerPolicy* aCrossOriginOpenerPolicy) override;
|
||||
NS_IMETHOD ComputeCrossOriginOpenerPolicy(
|
||||
|
||||
@@ -55,7 +55,6 @@ nsresult HttpConnectionUDP::Init(nsHttpConnectionInfo* info,
|
||||
LOG1(("HttpConnectionUDP::Init this=%p", this));
|
||||
NS_ENSURE_ARG_POINTER(info);
|
||||
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
|
||||
MOZ_ASSERT(dnsRecord || NS_FAILED(status));
|
||||
|
||||
mConnInfo = info;
|
||||
MOZ_ASSERT(mConnInfo);
|
||||
@@ -72,7 +71,6 @@ nsresult HttpConnectionUDP::Init(nsHttpConnectionInfo* info,
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDNSAddrRecord> dnsAddrRecord = do_QueryInterface(dnsRecord);
|
||||
MOZ_ASSERT(dnsAddrRecord);
|
||||
if (!dnsAddrRecord) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -405,18 +405,6 @@ void ObliviousHttpChannel::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {
|
||||
}
|
||||
}
|
||||
|
||||
void ObliviousHttpChannel::SetIPv6Disabled() {
|
||||
if (mInnerChannelInternal) {
|
||||
mInnerChannelInternal->SetIPv6Disabled();
|
||||
}
|
||||
}
|
||||
|
||||
void ObliviousHttpChannel::SetIPv4Disabled() {
|
||||
if (mInnerChannelInternal) {
|
||||
mInnerChannelInternal->SetIPv4Disabled();
|
||||
}
|
||||
}
|
||||
|
||||
void ObliviousHttpChannel::DisableAltDataCache() {
|
||||
if (mInnerChannelInternal) {
|
||||
mInnerChannelInternal->DisableAltDataCache();
|
||||
|
||||
@@ -3818,4 +3818,24 @@ void nsHttpConnectionMgr::CheckTransInPendingQueue(nsHttpTransaction* aTrans) {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool nsHttpConnectionMgr::AllowToRetryDifferentIPFamilyForHttp3(
|
||||
nsHttpConnectionInfo* ci, nsresult aError) {
|
||||
ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
|
||||
if (!ent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ent->AllowToRetryDifferentIPFamilyForHttp3(aError);
|
||||
}
|
||||
|
||||
void nsHttpConnectionMgr::SetRetryDifferentIPFamilyForHttp3(
|
||||
nsHttpConnectionInfo* ci, uint16_t aIPFamily) {
|
||||
ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
|
||||
if (!ent) {
|
||||
return;
|
||||
}
|
||||
|
||||
ent->SetRetryDifferentIPFamilyForHttp3(aIPFamily);
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
||||
@@ -191,6 +191,11 @@ class nsHttpConnectionMgr final : public HttpConnectionMgrShell,
|
||||
// connection
|
||||
bool BeConservativeIfProxied(nsIProxyInfo* proxy);
|
||||
|
||||
bool AllowToRetryDifferentIPFamilyForHttp3(nsHttpConnectionInfo* ci,
|
||||
nsresult aError);
|
||||
void SetRetryDifferentIPFamilyForHttp3(nsHttpConnectionInfo* ci,
|
||||
uint16_t aIPFamily);
|
||||
|
||||
protected:
|
||||
friend class ConnectionEntry;
|
||||
void IncrementActiveConnCount();
|
||||
|
||||
@@ -3534,4 +3534,9 @@ void nsHttpTransaction::SetIsForWebTransport(bool aIsForWebTransport) {
|
||||
mIsForWebTransport = aIsForWebTransport;
|
||||
}
|
||||
|
||||
void nsHttpTransaction::RemoveConnection() {
|
||||
MutexAutoLock lock(mLock);
|
||||
mConnection = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
||||
@@ -88,7 +88,7 @@ class nsHttpTransaction final : public nsAHttpTransaction,
|
||||
void MakeNonSticky() override { mCaps &= ~NS_HTTP_STICKY_CONNECTION; }
|
||||
void MakeRestartable() override { mCaps |= NS_HTTP_CONNECTION_RESTARTABLE; }
|
||||
void MakeNonRestartable() { mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE; }
|
||||
|
||||
void RemoveConnection();
|
||||
void SetIsHttp2Websocket(bool h2ws) override { mIsHttp2Websocket = h2ws; }
|
||||
bool IsHttp2Websocket() override { return mIsHttp2Websocket; }
|
||||
|
||||
|
||||
@@ -434,13 +434,11 @@ interface nsIHttpChannelInternal : nsISupports
|
||||
/**
|
||||
* The channel will be loaded over IPv6, disabling IPv4.
|
||||
*/
|
||||
[noscript, notxpcom, nostdcall]
|
||||
void setIPv4Disabled();
|
||||
|
||||
/**
|
||||
* The channel will be loaded over IPv4, disabling IPv6.
|
||||
*/
|
||||
[noscript, notxpcom, nostdcall]
|
||||
void setIPv6Disabled();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1098,18 +1098,6 @@ nsViewSourceChannel::PreferredAlternativeDataTypes() {
|
||||
return mEmptyArray;
|
||||
}
|
||||
|
||||
void nsViewSourceChannel::SetIPv4Disabled() {
|
||||
if (mHttpChannelInternal) {
|
||||
mHttpChannelInternal->SetIPv4Disabled();
|
||||
}
|
||||
}
|
||||
|
||||
void nsViewSourceChannel::SetIPv6Disabled() {
|
||||
if (mHttpChannelInternal) {
|
||||
mHttpChannelInternal->SetIPv6Disabled();
|
||||
}
|
||||
}
|
||||
|
||||
void nsViewSourceChannel::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {
|
||||
if (mHttpChannelInternal) {
|
||||
mHttpChannelInternal->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
|
||||
|
||||
283
netwerk/test/unit/test_http3_dns_retry.js
Normal file
283
netwerk/test/unit/test_http3_dns_retry.js
Normal file
@@ -0,0 +1,283 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var { setTimeout } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Timer.sys.mjs"
|
||||
);
|
||||
|
||||
let h2Port;
|
||||
let h3Port;
|
||||
let trrServer;
|
||||
|
||||
const { TestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/TestUtils.sys.mjs"
|
||||
);
|
||||
const certOverrideService = Cc[
|
||||
"@mozilla.org/security/certoverride;1"
|
||||
].getService(Ci.nsICertOverrideService);
|
||||
|
||||
add_setup(async function setup() {
|
||||
h2Port = Services.env.get("MOZHTTP2_PORT");
|
||||
Assert.notEqual(h2Port, null);
|
||||
Assert.notEqual(h2Port, "");
|
||||
|
||||
h3Port = Services.env.get("MOZHTTP3_PORT");
|
||||
Assert.notEqual(h3Port, null);
|
||||
Assert.notEqual(h3Port, "");
|
||||
|
||||
trr_test_setup();
|
||||
|
||||
if (mozinfo.socketprocess_networking) {
|
||||
Services.dns; // Needed to trigger socket process.
|
||||
await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
|
||||
}
|
||||
|
||||
Services.prefs.setIntPref("network.trr.mode", 2); // TRR first
|
||||
Services.prefs.setBoolPref("network.http.http3.enable", true);
|
||||
Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6);
|
||||
Services.prefs.setBoolPref(
|
||||
"network.http.http3.block_loopback_ipv6_addr",
|
||||
true
|
||||
);
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
trr_clear_prefs();
|
||||
Services.prefs.clearUserPref("network.http.http3.block_loopback_ipv6_addr");
|
||||
if (trrServer) {
|
||||
await trrServer.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function makeChan(url) {
|
||||
let chan = NetUtil.newChannel({
|
||||
uri: url,
|
||||
loadUsingSystemPrincipal: true,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
|
||||
}).QueryInterface(Ci.nsIHttpChannel);
|
||||
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
|
||||
return chan;
|
||||
}
|
||||
|
||||
function channelOpenPromise(chan, flags) {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async resolve => {
|
||||
function finish(req, buffer) {
|
||||
resolve([req, buffer]);
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
|
||||
false
|
||||
);
|
||||
}
|
||||
let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
|
||||
internal.setWaitForHTTPSSVCRecord();
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
|
||||
true
|
||||
);
|
||||
|
||||
chan.asyncOpen(new ChannelListener(finish, null, flags));
|
||||
});
|
||||
}
|
||||
|
||||
async function registerDoHAnswers(host, ipv4Answers, ipv6Answers, httpsRecord) {
|
||||
trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
|
||||
await trrServer.registerDoHAnswers(host, "HTTPS", {
|
||||
answers: httpsRecord,
|
||||
});
|
||||
|
||||
await trrServer.registerDoHAnswers(host, "AAAA", {
|
||||
answers: ipv6Answers,
|
||||
});
|
||||
|
||||
await trrServer.registerDoHAnswers(host, "A", {
|
||||
answers: ipv4Answers,
|
||||
});
|
||||
|
||||
Services.dns.clearCache(true);
|
||||
}
|
||||
|
||||
// Test if we retry IPv4 address for Http/3 properly.
|
||||
add_task(async function test_retry_with_ipv4() {
|
||||
let host = "test.http3_retry.com";
|
||||
let ipv4answers = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "A",
|
||||
flush: false,
|
||||
data: "127.0.0.1",
|
||||
},
|
||||
];
|
||||
// The UDP socket will return connection refused error because we set
|
||||
// "network.http.http3.block_loopback_ipv6_addr" to true.
|
||||
let ipv6answers = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "AAAA",
|
||||
flush: false,
|
||||
data: "::1",
|
||||
},
|
||||
];
|
||||
let httpsRecord = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: host,
|
||||
values: [
|
||||
{ key: "alpn", value: "h3-29" },
|
||||
{ key: "port", value: h3Port },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await registerDoHAnswers(host, ipv4answers, ipv6answers, httpsRecord);
|
||||
|
||||
let chan = makeChan(`https://${host}`);
|
||||
let [req] = await channelOpenPromise(chan);
|
||||
Assert.equal(req.protocolVersion, "h3-29");
|
||||
|
||||
await trrServer.stop();
|
||||
});
|
||||
|
||||
add_task(async function test_retry_with_ipv4_disabled() {
|
||||
let host = "test.http3_retry_ipv4_blocked.com";
|
||||
let ipv4answers = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "A",
|
||||
flush: false,
|
||||
data: "127.0.0.1",
|
||||
},
|
||||
];
|
||||
// The UDP socket will return connection refused error because we set
|
||||
// "network.http.http3.block_loopback_ipv6_addr" to true.
|
||||
let ipv6answers = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "AAAA",
|
||||
flush: false,
|
||||
data: "::1",
|
||||
},
|
||||
];
|
||||
let httpsRecord = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: host,
|
||||
values: [
|
||||
{ key: "alpn", value: "h3-29" },
|
||||
{ key: "port", value: h3Port },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await registerDoHAnswers(host, ipv4answers, ipv6answers, httpsRecord);
|
||||
|
||||
let chan = makeChan(`https://${host}`);
|
||||
chan.QueryInterface(Ci.nsIHttpChannelInternal);
|
||||
chan.setIPv4Disabled();
|
||||
|
||||
await channelOpenPromise(chan, CL_EXPECT_FAILURE);
|
||||
await trrServer.stop();
|
||||
});
|
||||
|
||||
// See bug 1837252. There is no way to observe the outcome of this test, because
|
||||
// the crash in bug 1837252 is only triggered by speculative connection.
|
||||
// The outcome of this test is no crash.
|
||||
add_task(async function test_retry_with_ipv4_failed() {
|
||||
let host = "test.http3_retry_failed.com";
|
||||
// Return a wrong answer intentionally.
|
||||
let ipv4answers = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "AAAA",
|
||||
flush: false,
|
||||
data: "127.0.0.1",
|
||||
},
|
||||
];
|
||||
// The UDP socket will return connection refused error because we set
|
||||
// "network.http.http3.block_loopback_ipv6_addr" to true.
|
||||
let ipv6answers = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "AAAA",
|
||||
flush: false,
|
||||
data: "::1",
|
||||
},
|
||||
];
|
||||
let httpsRecord = [
|
||||
{
|
||||
name: host,
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: host,
|
||||
values: [
|
||||
{ key: "alpn", value: "h3-29" },
|
||||
{ key: "port", value: h3Port },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await registerDoHAnswers(host, ipv4answers, ipv6answers, httpsRecord);
|
||||
|
||||
// This speculative connection is used to trigger the mechanism to retry
|
||||
// Http/3 connection with a IPv4 address.
|
||||
// We want to make the connection entry's IP preference known,
|
||||
// so DnsAndConnectSocket::mRetryWithDifferentIPFamily will be set to true
|
||||
// before the second speculative connection.
|
||||
let uri = Services.io.newURI(`https://test.http3_retry_failed.com`);
|
||||
Services.io.speculativeConnect(
|
||||
uri,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// When this speculative connection is created, the connection entry is
|
||||
// already set to prefer IPv4. Since we provided an invalid A response,
|
||||
// DnsAndConnectSocket::OnLookupComplete is called with an error.
|
||||
// Since DnsAndConnectSocket::mRetryWithDifferentIPFamily is true, we do
|
||||
// retry DNS lookup. During retry, we should not create UDP connection.
|
||||
Services.io.speculativeConnect(
|
||||
uri,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await trrServer.stop();
|
||||
});
|
||||
@@ -771,3 +771,8 @@ skip-if =
|
||||
skip-if =
|
||||
os == 'android'
|
||||
socketprocess_networking
|
||||
[test_http3_dns_retry.js]
|
||||
skip-if =
|
||||
os == 'android'
|
||||
os == 'win' && msix
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
|
||||
Reference in New Issue
Block a user