diff --git a/netwerk/protocol/http/AlternateServices.cpp b/netwerk/protocol/http/AlternateServices.cpp index 89cb2dabaeb7..6ef309c4daf9 100644 --- a/netwerk/protocol/http/AlternateServices.cpp +++ b/netwerk/protocol/http/AlternateServices.cpp @@ -160,9 +160,7 @@ void AltSvcMapping::ProcessHeader( SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo(); if (!(NS_SUCCEEDED(spdyInfo->GetNPNIndex(npnToken, &spdyIndex)) && spdyInfo->ProtocolEnabled(spdyIndex)) && - !(isHttp3 && gHttpHandler->IsHttp3Enabled() && - !gHttpHandler->IsHttp3Excluded(hostname.IsEmpty() ? originHost - : hostname))) { + !(isHttp3 && gHttpHandler->IsHttp3Enabled())) { LOG(("Alt Svc unknown protocol %s, ignoring", npnToken.get())); continue; } @@ -944,8 +942,7 @@ already_AddRefed AltSvcCache::LookupMapping( if (rv->IsHttp3() && (!gHttpHandler->IsHttp3Enabled() || - !gHttpHandler->IsHttp3VersionSupported(rv->NPNToken()) || - gHttpHandler->IsHttp3Excluded(rv->AlternateHost()))) { + !gHttpHandler->IsHttp3VersionSupported(rv->NPNToken()))) { // If Http3 is disabled or the version not supported anymore, remove the // mapping. mStorage->Remove( diff --git a/netwerk/protocol/http/Http3Session.cpp b/netwerk/protocol/http/Http3Session.cpp index 6835d1e0790b..12ffd38018fb 100644 --- a/netwerk/protocol/http/Http3Session.cpp +++ b/netwerk/protocol/http/Http3Session.cpp @@ -73,7 +73,8 @@ Http3Session::Http3Session() gHttpHandler->ConnMgr()->CurrentTopLevelOuterContentWindowId(); } -nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo, +nsresult Http3Session::Init(const nsACString& aOrigin, + const nsACString& aAlpnToken, nsISocketTransport* aSocketTransport, HttpConnectionUDP* readerWriter) { LOG3(("Http3Session::Init %p", this)); @@ -82,7 +83,7 @@ nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo, MOZ_ASSERT(aSocketTransport); MOZ_ASSERT(readerWriter); - mConnInfo = aConnInfo->Clone(); + mAlpnToken = aAlpnToken; mSocketTransport = aSocketTransport; nsCOMPtr info; @@ -136,14 +137,14 @@ nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo, LOG3( ("Http3Session::Init origin=%s, alpn=%s, selfAddr=%s, peerAddr=%s," " qpack table size=%u, max blocked streams=%u [this=%p]", - PromiseFlatCString(mConnInfo->GetOrigin()).get(), - PromiseFlatCString(mConnInfo->GetNPNToken()).get(), selfAddrStr.get(), - peerAddrStr.get(), gHttpHandler->DefaultQpackTableSize(), + PromiseFlatCString(aOrigin).get(), PromiseFlatCString(aAlpnToken).get(), + selfAddrStr.get(), peerAddrStr.get(), + gHttpHandler->DefaultQpackTableSize(), gHttpHandler->DefaultHttp3MaxBlockedStreams(), this)); nsresult rv = NeqoHttp3Conn::Init( - mConnInfo->GetOrigin(), mConnInfo->GetNPNToken(), selfAddrStr, - peerAddrStr, gHttpHandler->DefaultQpackTableSize(), + aOrigin, aAlpnToken, selfAddrStr, peerAddrStr, + gHttpHandler->DefaultQpackTableSize(), gHttpHandler->DefaultHttp3MaxBlockedStreams(), gHttpHandler->Http3QlogDir(), getter_AddRefs(mHttp3Connection)); if (NS_FAILED(rv)) { @@ -186,11 +187,6 @@ nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo, void Http3Session::Shutdown() { MOZ_ASSERT(OnSocketThread(), "not on socket thread"); - if ((mBeforeConnectedError || (mError == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR)) && - (mError != mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN))) { - gHttpHandler->ExcludeHttp3(mConnInfo); - } - for (auto iter = mStreamTransactionHash.Iter(); !iter.Done(); iter.Next()) { RefPtr stream = iter.Data(); @@ -199,12 +195,10 @@ void Http3Session::Shutdown() { // The transaction restart code path will remove AltSvc mapping and the // direct path will be used. MOZ_ASSERT(NS_FAILED(mError)); - stream->Close(NS_ERROR_NET_RESET); + stream->Close(mError); } else if (!stream->HasStreamId()) { - if (NS_SUCCEEDED(mError)) { - // Connection has not been started yet. We can restart it. - stream->Transaction()->DoNotRemoveAltSvc(); - } + // Connection has not been started yet. We can restart it. + stream->Transaction()->DoNotRemoveAltSvc(); stream->Close(NS_ERROR_NET_RESET); } else if (stream->RecvdData()) { stream->Close(NS_ERROR_NET_PARTIAL_TRANSFER); @@ -842,8 +836,7 @@ void Http3Session::ResetRecvd(uint64_t aStreamId, uint64_t aError) { if (aError == HTTP3_APP_ERROR_VERSION_FALLBACK) { // We will restart the request and the alt-svc will be removed // automatically. - // Also disable http3 we want http1.1. - stream->Transaction()->DisableHttp3(); + // Also disable spdy we want http/1.1. stream->Transaction()->DisableSpdy(); CloseStream(stream, NS_ERROR_NET_RESET); } else if (aError == HTTP3_APP_ERROR_REQUEST_REJECTED) { @@ -1481,11 +1474,11 @@ bool Http3Session::RealJoinConnection(const nsACString& hostname, int32_t port, bool joinedReturn = false; if (justKidding) { - rv = sslSocketControl->TestJoinConnection(mConnInfo->GetNPNToken(), - hostname, port, &isJoined); + rv = sslSocketControl->TestJoinConnection(mAlpnToken, hostname, port, + &isJoined); } else { - rv = sslSocketControl->JoinConnection(mConnInfo->GetNPNToken(), hostname, - port, &isJoined); + rv = + sslSocketControl->JoinConnection(mAlpnToken, hostname, port, &isJoined); } if (NS_SUCCEEDED(rv) && isJoined) { joinedReturn = true; diff --git a/netwerk/protocol/http/Http3Session.h b/netwerk/protocol/http/Http3Session.h index 6fe980cea083..610b2f57d436 100644 --- a/netwerk/protocol/http/Http3Session.h +++ b/netwerk/protocol/http/Http3Session.h @@ -46,7 +46,7 @@ class Http3Session final : public nsAHttpTransaction, NS_DECL_NSAHTTPSEGMENTWRITER Http3Session(); - nsresult Init(const nsHttpConnectionInfo* aConnInfo, + nsresult Init(const nsACString& aOrigin, const nsACString& aAlpnToken, nsISocketTransport* aSocketTransport, HttpConnectionUDP* readerWriter); @@ -206,8 +206,6 @@ class Http3Session final : public nsAHttpTransaction, // NS_NET_STATUS_CONNECTED_TO event will be created by the Http3Session. // We want to propagate it to the first transaction. RefPtr mFirstHttpTransaction; - - RefPtr mConnInfo; }; NS_DEFINE_STATIC_IID_ACCESSOR(Http3Session, NS_HTTP3SESSION_IID); diff --git a/netwerk/protocol/http/HttpConnectionUDP.cpp b/netwerk/protocol/http/HttpConnectionUDP.cpp index dec98bfef68a..f9ac4b418ef3 100644 --- a/netwerk/protocol/http/HttpConnectionUDP.cpp +++ b/netwerk/protocol/http/HttpConnectionUDP.cpp @@ -115,7 +115,8 @@ nsresult HttpConnectionUDP::Init( MOZ_ASSERT(mConnInfo->IsHttp3()); mHttp3Session = new Http3Session(); - nsresult rv = mHttp3Session->Init(mConnInfo, mSocketTransport, this); + nsresult rv = mHttp3Session->Init( + mConnInfo->GetOrigin(), mConnInfo->GetNPNToken(), mSocketTransport, this); if (NS_FAILED(rv)) { LOG( ("HttpConnectionUDP::Init mHttp3Session->Init failed " diff --git a/netwerk/protocol/http/TRRServiceChannel.cpp b/netwerk/protocol/http/TRRServiceChannel.cpp index faa13216a273..e707038fc83a 100644 --- a/netwerk/protocol/http/TRRServiceChannel.cpp +++ b/netwerk/protocol/http/TRRServiceChannel.cpp @@ -396,7 +396,8 @@ nsresult TRRServiceChannel::BeginConnect() { mAllowAltSvc = XRE_IsParentProcess() && mAllowAltSvc; bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo); bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo && - !(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative; + !(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative && + !gHttpHandler->IsHttp3Excluded(connInfo); RefPtr mapping; if (!mConnectionInfo && mAllowAltSvc && // per channel diff --git a/netwerk/protocol/http/nsAHttpTransaction.h b/netwerk/protocol/http/nsAHttpTransaction.h index 36992e2b9893..96f3e05cfcbe 100644 --- a/netwerk/protocol/http/nsAHttpTransaction.h +++ b/netwerk/protocol/http/nsAHttpTransaction.h @@ -178,7 +178,6 @@ class nsAHttpTransaction : public nsSupportsWeakReference { } virtual void DisableSpdy() {} - virtual void DisableHttp3() {} virtual void MakeNonSticky() {} virtual void ReuseConnectionOnRestartOK(bool) {} diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 83147029aa12..d9162796e11b 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -6672,7 +6672,7 @@ nsresult nsHttpChannel::BeginConnect() { } bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo && !(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative && - mAllowHttp3; + !gHttpHandler->IsHttp3Excluded(connInfo) && mAllowHttp3; // No need to lookup HTTPSSVC record if we already have one. mUseHTTPSSVC = diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 9ad4e2bfaa22..ce5a1a228541 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -2851,15 +2851,15 @@ void nsHttpHandler::ExcludeHttp3(const nsHttpConnectionInfo* ci) { MOZ_ASSERT(OnSocketThread(), "not on socket thread"); mConnMgr->ExcludeHttp3(ci); - if (!mExcludedHttp3Origins.Contains(ci->GetRoutedHost())) { + if (!mExcludedHttp3Origins.Contains(ci->GetOrigin())) { MutexAutoLock lock(mHttpExclusionLock); - mExcludedHttp3Origins.PutEntry(ci->GetRoutedHost()); + mExcludedHttp3Origins.PutEntry(ci->GetOrigin()); } } -bool nsHttpHandler::IsHttp3Excluded(const nsACString& aRoutedHost) { +bool nsHttpHandler::IsHttp3Excluded(const nsHttpConnectionInfo* ci) { MutexAutoLock lock(mHttpExclusionLock); - return mExcludedHttp3Origins.Contains(aRoutedHost); + return mExcludedHttp3Origins.Contains(ci->GetOrigin()); } HttpTrafficAnalyzer* nsHttpHandler::GetHttpTrafficAnalyzer() { diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 36aa0e88bc72..6880bb3ec3e4 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -855,7 +855,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, void ExcludeHttp2(const nsHttpConnectionInfo* ci); [[nodiscard]] bool IsHttp2Excluded(const nsHttpConnectionInfo* ci); void ExcludeHttp3(const nsHttpConnectionInfo* ci); - [[nodiscard]] bool IsHttp3Excluded(const nsACString& aRoutedHost); + [[nodiscard]] bool IsHttp3Excluded(const nsHttpConnectionInfo* ci); private: nsTHashtable mExcludedHttp2Origins; diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index 5687e9cf497a..6ebdf278ae02 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -2413,11 +2413,6 @@ void nsHttpTransaction::DisableHttp3() { // After CloneAsDirectRoute(), http3 will be disabled. RefPtr connInfo; mConnInfo->CloneAsDirectRoute(getter_AddRefs(connInfo)); - if (mRequestHead) { - DebugOnly rv = - mRequestHead->SetHeader(nsHttp::Alternate_Service_Used, "0"_ns); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } MOZ_ASSERT(!connInfo->IsHttp3()); mConnInfo.swap(connInfo); } @@ -2808,11 +2803,6 @@ nsresult nsHttpTransaction::RestartOnFastOpenError() { RefPtr ci; mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci)); mConnInfo = ci; - if (mRequestHead) { - DebugOnly rv = - mRequestHead->SetHeader(nsHttp::Alternate_Service_Used, "0"_ns); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } } mEarlyDataDisposition = EARLY_NONE; m0RTTInProgress = false; diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index e3be42a195c7..5655b483bac1 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -103,7 +103,7 @@ class nsHttpTransaction final : public nsAHttpTransaction, void RemoveDispatchedAsBlocking(); void DisableSpdy() override; - void DisableHttp3() override; + void DisableHttp3(); nsHttpTransaction* QueryHttpTransaction() override { return this; } diff --git a/netwerk/test/http3server/src/main.rs b/netwerk/test/http3server/src/main.rs index 799dbca681bf..5ec410597191 100644 --- a/netwerk/test/http3server/src/main.rs +++ b/netwerk/test/http3server/src/main.rs @@ -297,23 +297,6 @@ impl HttpServer for Server { } } -#[derive(Default)] -struct NonRespondingServer {} - -impl ::std::fmt::Display for NonRespondingServer { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "NonRespondingServer") - } -} - -impl HttpServer for NonRespondingServer { - fn process(&mut self, _dgram: Option) -> Output { - Output::None - } - - fn process_events(&mut self) {} -} - fn emit_packet(socket: &UdpSocket, out_dgram: Datagram) { let sent = socket .send_to(&out_dgram, &out_dgram.destination()) @@ -378,12 +361,6 @@ fn read_dgram( } } -enum ServerType { - Http3, - Http3Fail, - Http3NoResponse, -} - struct ServersRunner { hosts: Vec, poll: Poll, @@ -408,21 +385,19 @@ impl ServersRunner { } pub fn init(&mut self) { - self.add_new_socket(0, ServerType::Http3); - self.add_new_socket(1, ServerType::Http3Fail); - self.add_new_socket(3, ServerType::Http3NoResponse); + self.add_new_socket(0, true); + self.add_new_socket(1, false); println!( - "HTTP3 server listening on ports {}, {} and {}", + "HTTP3 server listening on ports {} and {}", self.hosts[0].port(), - self.hosts[1].port(), - self.hosts[2].port() + self.hosts[1].port() ); self.poll .register(&self.timer, TIMER_TOKEN, Ready::readable(), PollOpt::edge()) .unwrap(); } - fn add_new_socket(&mut self, count: usize, server_type: ServerType) -> u16 { + fn add_new_socket(&mut self, count: usize, http3: bool) -> u16 { let addr = "127.0.0.1:0".parse().unwrap(); let socket = match UdpSocket::bind(&addr) { @@ -454,17 +429,17 @@ impl ServersRunner { self.sockets.push(socket); self.servers - .insert(local_addr, (self.create_server(server_type), None)); + .insert(local_addr, (self.create_server(http3), None)); local_addr.port() } - fn create_server(&self, server_type: ServerType) -> Box { + fn create_server(&self, http3: bool) -> Box { let anti_replay = AntiReplay::new(Instant::now(), Duration::from_secs(10), 7, 14) .expect("unable to setup anti-replay"); let cid_mgr = Rc::new(RefCell::new(FixedConnectionIdManager::new(10))); - match server_type { - ServerType::Http3 => Box::new(Http3TestServer::new( + if http3 { + Box::new(Http3TestServer::new( Http3Server::new( Instant::now(), &[" HTTP2 Test Cert"], @@ -478,8 +453,9 @@ impl ServersRunner { }, ) .expect("We cannot make a server!"), - )), - ServerType::Http3Fail => Box::new( + )) + } else { + Box::new( Server::new( Instant::now(), &[" HTTP2 Test Cert"], @@ -489,8 +465,7 @@ impl ServersRunner { cid_mgr, ) .expect("We cannot make a server!"), - ), - ServerType::Http3NoResponse => Box::new(NonRespondingServer::default()), + ) } } diff --git a/netwerk/test/unit/test_http3_error_before_connect.js b/netwerk/test/unit/test_http3_error_before_connect.js deleted file mode 100644 index 447c1fa45b85..000000000000 --- a/netwerk/test/unit/test_http3_error_before_connect.js +++ /dev/null @@ -1,121 +0,0 @@ -/* 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"; - -let httpsUri; - -registerCleanupFunction(async () => { - Services.prefs.clearUserPref("network.http.http3.enabled"); - Services.prefs.clearUserPref("network.dns.localDomains"); - Services.prefs.clearUserPref("network.dns.disableIPv6"); - Services.prefs.clearUserPref( - "network.http.http3.alt-svc-mapping-for-testing" - ); - dump("cleanup done\n"); -}); - -function chanPromise(chan, listener) { - return new Promise(resolve => { - function finish() { - resolve(); - } - listener.finish = finish; - chan.asyncOpen(listener); - }); -} - -function makeChan() { - let chan = NetUtil.newChannel({ - uri: httpsUri, - loadUsingSystemPrincipal: true, - }).QueryInterface(Ci.nsIHttpChannel); - chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; - return chan; -} - -add_task(async function test_setup() { - let env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); - - let h2Port = env.get("MOZHTTP2_PORT"); - Assert.notEqual(h2Port, null); - let h3Port = env.get("MOZHTTP3_PORT_NO_RESPONSE"); - Assert.notEqual(h3Port, null); - Assert.notEqual(h3Port, ""); - - Services.prefs.setBoolPref("network.http.http3.enabled", true); - Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com"); - Services.prefs.setBoolPref("network.dns.disableIPv6", true); - // Set AltSvc to point to not existing HTTP3 server on port 443 - Services.prefs.setCharPref( - "network.http.http3.alt-svc-mapping-for-testing", - "foo.example.com;h3-27=:" + h3Port - ); - - let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( - Ci.nsIX509CertDB - ); - addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); - - httpsUri = "https://foo.example.com:" + h2Port + "/"; -}); - -add_task(async function test_fatal_stream_error() { - let result = 1; - // We need to loop here because we need to wait for AltSvc storage to - // to be started. - // We also do not have a way to verify that HTTP3 has been tried, because - // the fallback is automatic, so try a couple of times. - do { - // We need to close HTTP2 connections, otherwise our connection pooling - // will dispatch the request over already existing HTTP2 connection. - Services.obs.notifyObservers(null, "net:prune-all-connections"); - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); - result++; - } while (result < 5); -}); - -let CheckOnlyHttp2Listener = function() {}; - -CheckOnlyHttp2Listener.prototype = { - onStartRequest: function testOnStartRequest(request) {}, - - onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) { - read_stream(stream, cnt); - }, - - onStopRequest: function testOnStopRequest(request, status) { - Assert.equal(status, Cr.NS_OK); - let httpVersion = ""; - try { - httpVersion = request.protocolVersion; - } catch (e) {} - Assert.equal(httpVersion, "h2"); - - let routed = "NA"; - try { - routed = request.getRequestHeader("Alt-Used"); - } catch (e) {} - dump("routed is " + routed + "\n"); - Assert.ok(routed === "0" || routed === "NA"); - this.finish(); - }, -}; - -add_task(async function test_no_http3_after_error() { - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); -}); - -// also after all connections are closed. -add_task(async function test_no_http3_after_error2() { - Services.obs.notifyObservers(null, "net:prune-all-connections"); - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); -}); diff --git a/netwerk/test/unit/test_http3_fatal_stream_error.js b/netwerk/test/unit/test_http3_fatal_stream_error.js index 2ad8a72ad959..10281c51e81b 100644 --- a/netwerk/test/unit/test_http3_fatal_stream_error.js +++ b/netwerk/test/unit/test_http3_fatal_stream_error.js @@ -1,147 +1,141 @@ -/* 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"; -let httpsUri; +let h3Port; +let h3Route; +let h3AltSvc; +let prefs; +let httpsOrigin; -registerCleanupFunction(async () => { - Services.prefs.clearUserPref("network.http.http3.enabled"); - Services.prefs.clearUserPref("network.dns.localDomains"); - Services.prefs.clearUserPref("network.dns.disableIPv6"); - Services.prefs.clearUserPref( - "network.http.http3.alt-svc-mapping-for-testing" +let tests = [ + // The altSvc storage may not be up imediately, therefore run test_no_altsvc_pref + // for a couple times to wait for the storage. + test_no_altsvc_pref, + test_no_altsvc_pref, + test_no_altsvc_pref, + test_protocol_error, + testsDone, +]; + +let current_test = 0; + +function run_next_test() { + if (current_test < tests.length) { + dump("starting test number " + current_test + "\n"); + tests[current_test](); + current_test++; + } +} + +function run_test() { + let env = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment ); - dump("cleanup done\n"); -}); + h3Port = env.get("MOZHTTP3_PORT_FAILED"); + Assert.notEqual(h3Port, null); + Assert.notEqual(h3Port, ""); + h3AltSvc = ":" + h3Port; -let Http3FailedListener = function() {}; + h3Route = "foo.example.com:" + h3Port; + do_get_profile(); + prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); -Http3FailedListener.prototype = { - onStartRequest: function testOnStartRequest(request) {}, + prefs.setBoolPref("network.http.http3.enabled", true); + prefs.setCharPref("network.dns.localDomains", "foo.example.com"); + prefs.setBoolPref("network.dns.disableIPv6", true); + + // The certificate for the http3server server is for foo.example.com and + // is signed by http2-ca.pem so add that cert to the trust list as a + // signing cert. + let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( + Ci.nsIX509CertDB + ); + addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); + httpsOrigin = "https://foo.example.com/"; + + run_next_test(); +} + +let Http3CheckListener = function() {}; + +Http3CheckListener.prototype = { + expectedRoute: "", + expectedStatus: Cr.NS_OK, + + onStartRequest: function testOnStartRequest(request) { + Assert.ok(request instanceof Ci.nsIHttpChannel); + Assert.equal(request.status, this.expectedStatus); + if (Components.isSuccessCode(this.expectedStatus)) { + Assert.equal(request.responseStatus, 200); + } + }, onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) { - this.amount += cnt; read_stream(stream, cnt); }, onStopRequest: function testOnStopRequest(request, status) { - if (Components.isSuccessCode(status)) { - // This is still HTTP2 connection + Assert.equal(status, this.expectedStatus); + if (Components.isSuccessCode(this.expectedStatus)) { + Assert.equal(request.responseStatus, 200); + let routed = "NA"; + try { + routed = request.getRequestHeader("Alt-Used"); + } catch (e) {} + dump("routed is " + routed + "\n"); + + Assert.equal(routed, this.expectedRoute); + let httpVersion = ""; try { httpVersion = request.protocolVersion; } catch (e) {} - Assert.equal(httpVersion, "h2"); - this.finish(false); - } else { - Assert.equal(status, Cr.NS_ERROR_NET_PARTIAL_TRANSFER); - this.finish(true); + Assert.equal(httpVersion, "h3"); } + + run_next_test(); + do_test_finished(); }, }; -function chanPromise(chan, listener) { - return new Promise(resolve => { - function finish(result) { - resolve(result); - } - listener.finish = finish; - chan.asyncOpen(listener); - }); -} - -function makeChan() { +function makeChan(uri) { let chan = NetUtil.newChannel({ - uri: httpsUri, + uri, loadUsingSystemPrincipal: true, }).QueryInterface(Ci.nsIHttpChannel); chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; return chan; } -function altsvcSetupPromise(chan, listener) { - return new Promise(resolve => { - function finish(result) { - resolve(result); - } - listener.finish = finish; - chan.asyncOpen(listener); - }); +function test_no_altsvc_pref() { + dump("test_no_altsvc_pref"); + do_test_pending(); + + let chan = makeChan(httpsOrigin + "http3-test"); + let listener = new Http3CheckListener(); + listener.expectedStatus = Cr.NS_ERROR_CONNECTION_REFUSED; + chan.asyncOpen(listener); } -add_task(async function test_fatal_error() { - let env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); +function test_protocol_error() { + dump("test_protocol_error"); + do_test_pending(); - let h2Port = env.get("MOZHTTP2_PORT"); - Assert.notEqual(h2Port, null); - - let h3Port = env.get("MOZHTTP3_PORT_FAILED"); - Assert.notEqual(h3Port, null); - Assert.notEqual(h3Port, ""); - - Services.prefs.setBoolPref("network.http.http3.enabled", true); - Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com"); - Services.prefs.setBoolPref("network.dns.disableIPv6", true); - Services.prefs.setCharPref( + prefs.setCharPref( "network.http.http3.alt-svc-mapping-for-testing", - "foo.example.com;h3-27=:" + h3Port + "foo.example.com;h3-27=" + h3AltSvc ); - let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( - Ci.nsIX509CertDB - ); - addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); + let chan = makeChan(httpsOrigin + "wrong_frame_after_data_frame"); + let listener = new Http3CheckListener(); + listener.expectedRoute = h3Route; + listener.expectedStatus = Cr.NS_ERROR_NET_PARTIAL_TRANSFER; + chan.asyncOpen(listener); +} - httpsUri = "https://foo.example.com:" + h2Port + "/"; -}); - -add_task(async function test_fatal_stream_error() { - let result = false; - // We need to loop here because we need to wait for AltSvc storage to - // to be started. - do { - // We need to close HTTP2 connections, otherwise our connection pooling - // will dispatch the request over already existing HTTP2 connection. - Services.obs.notifyObservers(null, "net:prune-all-connections"); - let chan = makeChan(); - let listener = new Http3FailedListener(); - result = await altsvcSetupPromise(chan, listener); - } while (result === false); -}); - -let CheckOnlyHttp2Listener = function() {}; - -CheckOnlyHttp2Listener.prototype = { - onStartRequest: function testOnStartRequest(request) {}, - - onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) { - read_stream(stream, cnt); - }, - - onStopRequest: function testOnStopRequest(request, status) { - Assert.equal(status, Cr.NS_OK); - let httpVersion = ""; - try { - httpVersion = request.protocolVersion; - } catch (e) {} - Assert.equal(httpVersion, "h2"); - this.finish(false); - }, -}; - -add_task(async function test_no_http3_after_error() { - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); -}); - -// also after all connections are closed. -add_task(async function test_no_http3_after_error2() { - Services.obs.notifyObservers(null, "net:prune-all-connections"); - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); -}); +function testsDone() { + prefs.clearUserPref("network.http.http3.enabled"); + prefs.clearUserPref("network.dns.localDomains"); + prefs.clearUserPref("network.dns.disableIPv6"); + prefs.clearUserPref("network.http.http3.alt-svc-mapping-for-testing"); + dump("testDone\n"); +} diff --git a/netwerk/test/unit/test_http3_server_not_existing.js b/netwerk/test/unit/test_http3_server_not_existing.js deleted file mode 100644 index 3ca3a407cd92..000000000000 --- a/netwerk/test/unit/test_http3_server_not_existing.js +++ /dev/null @@ -1,121 +0,0 @@ -/* 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"; - -let httpsUri; - -registerCleanupFunction(async () => { - Services.prefs.clearUserPref("network.http.http3.enabled"); - Services.prefs.clearUserPref("network.dns.localDomains"); - Services.prefs.clearUserPref("network.dns.disableIPv6"); - Services.prefs.clearUserPref( - "network.http.http3.alt-svc-mapping-for-testing" - ); - dump("cleanup done\n"); -}); - -function chanPromise(chan, listener) { - return new Promise(resolve => { - function finish() { - resolve(); - } - listener.finish = finish; - chan.asyncOpen(listener); - }); -} - -function makeChan() { - let chan = NetUtil.newChannel({ - uri: httpsUri, - loadUsingSystemPrincipal: true, - }).QueryInterface(Ci.nsIHttpChannel); - chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; - return chan; -} - -function altsvcSetupPromise(chan, listener) { - return new Promise(resolve => { - function finish(result) { - resolve(result); - } - listener.finish = finish; - chan.asyncOpen(listener); - }); -} - -add_task(async function test_fatal_error() { - let env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); - - let h2Port = env.get("MOZHTTP2_PORT"); - Assert.notEqual(h2Port, null); - - Services.prefs.setBoolPref("network.http.http3.enabled", true); - Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com"); - Services.prefs.setBoolPref("network.dns.disableIPv6", true); - // Set AltSvc to point to not existing HTTP3 server on port 443 - Services.prefs.setCharPref( - "network.http.http3.alt-svc-mapping-for-testing", - "foo.example.com;h3-27=:443" - ); - - let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( - Ci.nsIX509CertDB - ); - addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); - - httpsUri = "https://foo.example.com:" + h2Port + "/"; -}); - -add_task(async function test_fatal_stream_error() { - let result = 1; - // We need to loop here because we need to wait for AltSvc storage to - // to be started. - // We also do not have a way to verify that HTTP3 has been tried, because - // the fallback is automatic, so try a couple of times. - do { - // We need to close HTTP2 connections, otherwise our connection pooling - // will dispatch the request over already existing HTTP2 connection. - Services.obs.notifyObservers(null, "net:prune-all-connections"); - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); - result++; - } while (result < 5); -}); - -let CheckOnlyHttp2Listener = function() {}; - -CheckOnlyHttp2Listener.prototype = { - onStartRequest: function testOnStartRequest(request) {}, - - onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) { - read_stream(stream, cnt); - }, - - onStopRequest: function testOnStopRequest(request, status) { - Assert.equal(status, Cr.NS_OK); - let httpVersion = ""; - try { - httpVersion = request.protocolVersion; - } catch (e) {} - Assert.equal(httpVersion, "h2"); - this.finish(); - }, -}; - -add_task(async function test_no_http3_after_error() { - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); -}); - -// also after all connections are closed. -add_task(async function test_no_http3_after_error2() { - Services.obs.notifyObservers(null, "net:prune-all-connections"); - let chan = makeChan(); - let listener = new CheckOnlyHttp2Listener(); - await altsvcSetupPromise(chan, listener); -}); diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 6dec1c56829e..ba46fe8aaba6 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -474,17 +474,10 @@ skip-if = asan || tsan || os == 'win' || os =='android' skip-if = asan || tsan || os == 'win' || os =='android' [test_brotli_http.js] [test_altsvc_http3.js] -skip-if = true +skip-if = asan || tsan || os == 'win' || os =='android' [test_http3_fatal_stream_error.js] -skip-if = asan || tsan || os == 'win' || os =='android' || socketprocess_networking -run-sequentially = node server exceptions dont replay well +skip-if = asan || tsan || os == 'win' || os =='android' [test_http3_large_post.js] skip-if = asan || tsan || os == 'win' || os =='android' -[test_http3_error_before_connect.js] -skip-if = asan || tsan || os == 'win' || os =='android' || socketprocess_networking -run-sequentially = node server exceptions dont replay well -[test_http3_server_not_existing.js] -skip-if = asan || tsan || os == 'win' || os =='android' || socketprocess_networking -run-sequentially = node server exceptions dont replay well [test_cookie_ipv6.js] diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index 722ea274ef9f..30df0ae765f1 100755 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -1412,13 +1412,11 @@ class XPCShellTests(object): msg = process.stdout.readline() if "server listening" in msg: searchObj = re.search( - r"HTTP3 server listening on ports ([0-9]+), ([0-9]+) and ([0-9]+)", - msg, 0 + r"HTTP3 server listening on ports ([0-9]+) and ([0-9]+)", msg, 0 ) if searchObj: self.env["MOZHTTP3_PORT"] = searchObj.group(1) self.env["MOZHTTP3_PORT_FAILED"] = searchObj.group(2) - self.env["MOZHTTP3_PORT_NO_RESPONSE"] = searchObj.group(3) except OSError as e: # This occurs if the subprocess couldn't be started self.log.error("Could not run the http3 server: %s" % (str(e)))