summaryrefslogtreecommitdiffstats
path: root/netwerk
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-08 15:18:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-08 15:18:09 +0000
commit0cd6f26b6b8fcec2b43398fd831f6b9e0cb977e3 (patch)
tree673eec8dca4c4cfc5125dd4447f6608e589fa6b9 /netwerk
parentAdding debian version 115.8.0esr-1~deb12u1. (diff)
downloadfirefox-esr-0cd6f26b6b8fcec2b43398fd831f6b9e0cb977e3.tar.xz
firefox-esr-0cd6f26b6b8fcec2b43398fd831f6b9e0cb977e3.zip
Merging upstream version 115.9.0esr.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk')
-rw-r--r--netwerk/dns/effective_tld_names.dat111
-rw-r--r--netwerk/protocol/http/Http2Compression.cpp33
-rw-r--r--netwerk/protocol/http/Http2Compression.h9
-rw-r--r--netwerk/protocol/http/HttpChannelChild.cpp49
-rw-r--r--netwerk/protocol/http/HttpChannelChild.h10
-rw-r--r--netwerk/protocol/http/TlsHandshaker.cpp18
-rw-r--r--netwerk/protocol/http/nsITlsHandshakeListener.idl2
-rw-r--r--netwerk/test/unit/test_client_auth_with_proxy.js185
-rw-r--r--netwerk/test/unit/xpcshell.ini2
9 files changed, 367 insertions, 52 deletions
diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat
index be79353f30..c2b813fa68 100644
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -6710,7 +6710,7 @@ org.zw
// newGTLDs
-// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-01-24T15:14:29Z
+// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-03-06T15:14:58Z
// This list is auto-generated, don't edit it manually.
// aaa : American Automobile Association, Inc.
// https://www.iana.org/domains/root/db/aaa.html
@@ -7540,10 +7540,6 @@ college
// https://www.iana.org/domains/root/db/cologne.html
cologne
-// comcast : Comcast IP Holdings I, LLC
-// https://www.iana.org/domains/root/db/comcast.html
-comcast
-
// commbank : COMMONWEALTH BANK OF AUSTRALIA
// https://www.iana.org/domains/root/db/commbank.html
commbank
@@ -8360,10 +8356,6 @@ grocery
// https://www.iana.org/domains/root/db/group.html
group
-// guardian : The Guardian Life Insurance Company of America
-// https://www.iana.org/domains/root/db/guardian.html
-guardian
-
// gucci : Guccio Gucci S.p.a.
// https://www.iana.org/domains/root/db/gucci.html
gucci
@@ -10756,10 +10748,6 @@ xbox
// https://www.iana.org/domains/root/db/xerox.html
xerox
-// xfinity : Comcast IP Holdings I, LLC
-// https://www.iana.org/domains/root/db/xfinity.html
-xfinity
-
// xihuan : Beijing Qihu Keji Co., Ltd.
// https://www.iana.org/domains/root/db/xihuan.html
xihuan
@@ -11237,6 +11225,10 @@ graphox.us
// Submitted by Ofer Kalaora <postmaster@activetrail.com>
activetrail.biz
+// Adaptable.io : https://adaptable.io
+// Submitted by Mark Terrel <support@adaptable.io>
+adaptable.app
+
// Adobe : https://www.adobe.com/
// Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff <trieloff@adobe.com>
adobeaemcloud.com
@@ -12049,6 +12041,10 @@ appudo.net
// Submitted by Thomas Orozco <thomas@aptible.com>
on-aptible.com
+// Aquapal : https://aquapal.net/
+// Submitted by Aki Ueno <admin@aquapal.net>
+f5.si
+
// ASEINet : https://www.aseinet.com/
// Submitted by Asei SEKIGUCHI <mail@aseinet.com>
user.aseinet.ne.jp
@@ -12221,7 +12217,9 @@ mycd.eu
// Canva Pty Ltd : https://canva.com/
// Submitted by Joel Aquilina <publicsuffixlist@canva.com>
canva-apps.cn
+*.my.canvasite.cn
canva-apps.com
+*.my.canva.site
// Carrd : https://carrd.co
// Submitted by AJ <aj@carrd.co>
@@ -12413,6 +12411,10 @@ co.no
webhosting.be
hosting-cluster.nl
+// Convex : https://convex.dev/
+// Submitted by James Cowling <security@convex.dev>
+convex.site
+
// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/
// Submitted by George Georgievsky <gug@cctld.ru>
ac.ru
@@ -12434,10 +12436,18 @@ feste-ip.net
knx-server.net
static-access.net
+// cPanel L.L.C. : https://www.cpanel.net/
+// Submitted by Dustin Scherer <public.suffix@cpanel.net>
+*.cprapid.com
+
// Craynic, s.r.o. : http://www.craynic.com/
// Submitted by Ales Krajnik <ales.krajnik@craynic.com>
realm.cz
+// Crisp IM SAS : https://crisp.chat/
+// Submitted by Baptiste Jamin <hostmaster@crisp.chat>
+on.crisp.email
+
// Cryptonomic : https://cryptonomic.net/
// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net>
*.cryptonomic.net
@@ -12945,6 +12955,10 @@ e4.cz
easypanel.app
easypanel.host
+// EasyWP : https://www.easywp.com
+// Submitted by <infracloudteam@namecheap.com>
+*.ewp.live
+
// Elementor : Elementor Ltd.
// Submitted by Anton Barkan <antonb@elementor.com>
elementor.cloud
@@ -14163,6 +14177,10 @@ mintere.site
// Submitted by Grayson Martin <grayson.martin@mobileeducation.us>
forte.id
+// MODX Systems LLC : https://modx.com
+// Submitted by Elizabeth Southwell <elizabeth@modx.com>
+modx.dev
+
// Mozilla Corporation : https://mozilla.com
// Submitted by Ben Francis <bfrancis@mozilla.com>
mozilla-iot.org
@@ -14224,6 +14242,7 @@ jp.ngrok.io
sa.ngrok.io
us.ngrok.io
ngrok.pizza
+ngrok.pro
// Nicolaus Copernicus University in Torun - MSK TORMAN (https://www.man.torun.pl)
torun.pl
@@ -14639,6 +14658,8 @@ qbuser.com
// Rad Web Hosting: https://radwebhosting.com
// Submitted by Scott Claeys <s.claeys@radwebhosting.com>
cloudsite.builders
+myradweb.net
+servername.us
// Redgate Software: https://red-gate.com
// Submitted by Andrew Farries <andrew.farries@red-gate.com>
@@ -14705,11 +14726,40 @@ app.render.com
onrender.com
// Repl.it : https://repl.it
-// Submitted by Lincoln Bergeson <lincoln@replit.com>
+// Submitted by Lincoln Bergeson <psl@repl.it>
+replit.app
+id.replit.app
firewalledreplit.co
id.firewalledreplit.co
repl.co
id.repl.co
+replit.dev
+archer.replit.dev
+bones.replit.dev
+canary.replit.dev
+global.replit.dev
+hacker.replit.dev
+id.replit.dev
+janeway.replit.dev
+kim.replit.dev
+kira.replit.dev
+kirk.replit.dev
+odo.replit.dev
+paris.replit.dev
+picard.replit.dev
+pike.replit.dev
+prerelease.replit.dev
+reed.replit.dev
+riker.replit.dev
+sisko.replit.dev
+spock.replit.dev
+staging.replit.dev
+sulu.replit.dev
+tarpit.replit.dev
+teams.replit.dev
+tucker.replit.dev
+wesley.replit.dev
+worf.replit.dev
repl.run
// Resin.io : https://resin.io
@@ -14992,6 +15042,14 @@ srht.site
// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
stackhero-network.com
+// STACKIT : https://www.stackit.de/en/
+// Submitted by STACKIT-DNS Team (Simon Stier) <stackit-dns@mail.schwarz>
+runs.onstackit.cloud
+stackit.gg
+stackit.rocks
+stackit.run
+stackit.zone
+
// Staclar : https://staclar.com
// Submitted by Q Misell <q@staclar.com>
musician.io
@@ -15058,6 +15116,19 @@ myspreadshop.co.uk
// Submitted by Jacob Lee <jacob@stdlib.com>
api.stdlib.com
+// stereosense GmbH : https://www.involve.me
+// Submitted by Florian Burmann <publicsuffix@involve.me>
+feedback.ac
+forms.ac
+assessments.cx
+calculators.cx
+funnels.cx
+paynow.cx
+quizzes.cx
+researched.cx
+tests.cx
+surveys.so
+
// Storipress : https://storipress.com
// Submitted by Benno Liu <benno@storipress.com>
storipress.app
@@ -15066,6 +15137,12 @@ storipress.app
// Submitted by Philip Hutchins <hostmaster@storj.io>
storj.farm
+// Streak : https://streak.com
+// Submitted by Blake Kadatz <eng@streak.com>
+streak-link.com
+streaklinks.com
+streakusercontent.com
+
// Studenten Net Twente : http://www.snt.utwente.nl/
// Submitted by Silke Hofstra <syscom@snt.utwente.nl>
utwente.io
@@ -15128,6 +15205,7 @@ taifun-dns.de
// Submitted by David Anderson <danderson@tailscale.com>
beta.tailscale.net
ts.net
+*.c.ts.net
// TASK geographical domains (www.task.gda.pl/uslugi/dns)
gda.pl
@@ -15363,6 +15441,11 @@ v.ua
// Submitted by Masayuki Note <masa@blade.wafflecell.com>
wafflecell.com
+// Webflow, Inc. : https://www.webflow.com
+// Submitted by Webflow Security Team <security@webflow.com>
+webflow.io
+webflowtest.io
+
// WebHare bv: https://www.webhare.com/
// Submitted by Arnold Hendriks <info@webhare.com>
*.webhare.dev
diff --git a/netwerk/protocol/http/Http2Compression.cpp b/netwerk/protocol/http/Http2Compression.cpp
index 4d16e9e532..b95883f13f 100644
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -61,6 +61,7 @@ class HpackDynamicTableReporter final : public nsIMemoryReporter {
NS_IMETHOD
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) override {
+ MutexAutoLock lock(mMutex);
if (mCompressor) {
MOZ_COLLECT_REPORT("explicit/network/hpack/dynamic-tables", KIND_HEAP,
UNITS_BYTES,
@@ -75,7 +76,8 @@ class HpackDynamicTableReporter final : public nsIMemoryReporter {
~HpackDynamicTableReporter() = default;
- Http2BaseCompressor* mCompressor;
+ Mutex mMutex{"HpackDynamicTableReporter"};
+ Http2BaseCompressor* mCompressor MOZ_GUARDED_BY(mMutex);
friend class Http2BaseCompressor;
};
@@ -187,13 +189,18 @@ nvFIFO::~nvFIFO() { Clear(); }
void nvFIFO::AddElement(const nsCString& name, const nsCString& value) {
nvPair* pair = new nvPair(name, value);
mByteCount += pair->Size();
+ MutexAutoLock lock(mMutex);
mTable.PushFront(pair);
}
void nvFIFO::AddElement(const nsCString& name) { AddElement(name, ""_ns); }
void nvFIFO::RemoveElement() {
- nvPair* pair = mTable.Pop();
+ nvPair* pair = nullptr;
+ {
+ MutexAutoLock lock(mMutex);
+ pair = mTable.Pop();
+ }
if (pair) {
mByteCount -= pair->Size();
delete pair;
@@ -212,6 +219,7 @@ size_t nvFIFO::StaticLength() const { return gStaticHeaders->GetSize(); }
void nvFIFO::Clear() {
mByteCount = 0;
+ MutexAutoLock lock(mMutex);
while (mTable.GetSize()) {
delete mTable.Pop();
}
@@ -244,20 +252,27 @@ Http2BaseCompressor::~Http2BaseCompressor() {
Telemetry::Accumulate(mPeakCountID, mPeakCount);
}
UnregisterStrongMemoryReporter(mDynamicReporter);
- mDynamicReporter->mCompressor = nullptr;
+ {
+ MutexAutoLock lock(mDynamicReporter->mMutex);
+ mDynamicReporter->mCompressor = nullptr;
+ }
mDynamicReporter = nullptr;
}
+size_t nvFIFO::SizeOfDynamicTable(mozilla::MallocSizeOf aMallocSizeOf) const {
+ size_t size = 0;
+ MutexAutoLock lock(mMutex);
+ for (const auto elem : mTable) {
+ size += elem->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ return size;
+}
+
void Http2BaseCompressor::ClearHeaderTable() { mHeaderTable.Clear(); }
size_t Http2BaseCompressor::SizeOfExcludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
- size_t size = 0;
- for (uint32_t i = mHeaderTable.StaticLength(); i < mHeaderTable.Length();
- ++i) {
- size += mHeaderTable[i]->SizeOfIncludingThis(aMallocSizeOf);
- }
- return size;
+ return mHeaderTable.SizeOfDynamicTable(aMallocSizeOf);
}
void Http2BaseCompressor::MakeRoom(uint32_t amount, const char* direction) {
diff --git a/netwerk/protocol/http/Http2Compression.h b/netwerk/protocol/http/Http2Compression.h
index dd98584607..ad47949938 100644
--- a/netwerk/protocol/http/Http2Compression.h
+++ b/netwerk/protocol/http/Http2Compression.h
@@ -13,6 +13,7 @@
#include "nsDeque.h"
#include "nsString.h"
#include "mozilla/Telemetry.h"
+#include "mozilla/Mutex.h"
namespace mozilla {
namespace net {
@@ -47,10 +48,18 @@ class nvFIFO {
size_t StaticLength() const;
void Clear();
const nvPair* operator[](size_t index) const;
+ size_t SizeOfDynamicTable(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
uint32_t mByteCount{0};
nsDeque<nvPair> mTable;
+
+ // This mutex is held when adding or removing elements in the table
+ // and when accessing the table from the main thread (in SizeOfDynamicTable)
+ // Since the operator[] and other const methods are always called
+ // on the socket thread, they don't need to lock the mutex.
+ // Mutable so it can be locked in SizeOfDynamicTable which is const
+ mutable Mutex mMutex MOZ_UNANNOTATED{"nvFIFO"};
};
class HpackDynamicTableReporter;
diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
index 34e7a2cf0d..240bb14433 100644
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -87,7 +87,8 @@ HttpChannelChild::HttpChannelChild()
mIsLastPartOfMultiPart(false),
mSuspendForWaitCompleteRedirectSetup(false),
mRecvOnStartRequestSentCalled(false),
- mSuspendedByWaitingForPermissionCookie(false) {
+ mSuspendedByWaitingForPermissionCookie(false),
+ mAlreadyReleased(false) {
LOG(("Creating HttpChannelChild @%p\n", this));
mChannelCreationTime = PR_Now();
@@ -199,12 +200,16 @@ NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
// We don't have a listener when AsyncOpen has failed or when this channel
// has been sucessfully redirected.
if (MOZ_LIKELY(LoadOnStartRequestCalled() && LoadOnStopRequestCalled()) ||
- !mListener) {
+ !mListener || mAlreadyReleased) {
NS_LOG_RELEASE(this, 0, "HttpChannelChild");
delete this;
return 0;
}
+ // This ensures that when the refcount goes to 0 again, we don't dispatch
+ // yet another runnable and get in a loop.
+ mAlreadyReleased = true;
+
// This makes sure we fulfill the stream listener contract all the time.
if (NS_SUCCEEDED(mStatus)) {
mStatus = NS_ERROR_ABORT;
@@ -223,27 +228,11 @@ NS_IMETHODIMP_(MozExternalRefCountType) HttpChannelChild::Release() {
// 3) Finally, we turn the reference into a regular smart pointer.
RefPtr<HttpChannelChild> channel = dont_AddRef(this);
-
- // This runnable will create a strong reference to |this|.
- NS_DispatchToMainThread(
- NewRunnableMethod("~HttpChannelChild>DoNotifyListener", channel,
- &HttpChannelChild::DoNotifyListener));
-
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "~HttpChannelChild>DoNotifyListener",
+ [chan = std::move(channel)] { chan->DoNotifyListener(false); }));
// If NS_DispatchToMainThread failed then we're going to leak the runnable,
// and thus the channel, so there's no need to do anything else.
-
- // We should have already done any special handling for the refcount = 1
- // case when the refcount first went from 2 to 1. We don't want it to happen
- // when |channel| is destroyed.
- MOZ_ASSERT(!mKeptAlive || !CanSend());
-
- // XXX If std::move(channel) is allowed, then we don't have to have extra
- // checks for the refcount going from 2 to 1. See bug 1680217.
-
- // This will release the stabilization refcount, which is necessary to avoid
- // a leak.
- channel = nullptr;
-
return mRefCnt;
}
@@ -1233,7 +1222,7 @@ void HttpChannelChild::NotifyOrReleaseListeners(nsresult rv) {
DoNotifyListener();
}
-void HttpChannelChild::DoNotifyListener() {
+void HttpChannelChild::DoNotifyListener(bool aUseEventQueue) {
LOG(("HttpChannelChild::DoNotifyListener this=%p", this));
MOZ_ASSERT(NS_IsMainThread());
@@ -1246,16 +1235,20 @@ void HttpChannelChild::DoNotifyListener() {
if (mListener && !LoadOnStartRequestCalled()) {
nsCOMPtr<nsIStreamListener> listener = mListener;
- StoreOnStartRequestCalled(
- true); // avoid reentrancy bugs by setting this now
+ // avoid reentrancy bugs by setting this now
+ StoreOnStartRequestCalled(true);
listener->OnStartRequest(this);
}
StoreOnStartRequestCalled(true);
- mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
- this, [self = UnsafePtr<HttpChannelChild>(this)] {
- self->ContinueDoNotifyListener();
- }));
+ if (aUseEventQueue) {
+ mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
+ this, [self = UnsafePtr<HttpChannelChild>(this)] {
+ self->ContinueDoNotifyListener();
+ }));
+ } else {
+ ContinueDoNotifyListener();
+ }
}
void HttpChannelChild::ContinueDoNotifyListener() {
diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h
index c88d30023c..6a54ce390a 100644
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -395,6 +395,12 @@ class HttpChannelChild final : public PHttpChannelChild,
// permission or cookie. That is, RecvOnStartRequestSent is received.
uint8_t mSuspendedByWaitingForPermissionCookie : 1;
+ // HttpChannelChild::Release has some special logic that makes sure
+ // OnStart/OnStop are always called when releasing the channel.
+ // But we have to make sure we only do this once - otherwise we could
+ // get stuck in a loop.
+ uint8_t mAlreadyReleased : 1;
+
void CleanupRedirectingChannel(nsresult rv);
// Calls OnStartRequest and/or OnStopRequest on our listener in case we didn't
@@ -427,7 +433,9 @@ class HttpChannelChild final : public PHttpChannelChild,
const ResourceTimingStructArgs& timing);
void Redirect3Complete();
void DeleteSelf();
- void DoNotifyListener();
+ // aUseEventQueue should only be false when called from
+ // HttpChannelChild::Release to make sure OnStopRequest is called syncly.
+ void DoNotifyListener(bool aUseEventQueue = true);
void ContinueDoNotifyListener();
void OnAfterLastPart(const nsresult& aStatus);
void MaybeConnectToSocketProcess();
diff --git a/netwerk/protocol/http/TlsHandshaker.cpp b/netwerk/protocol/http/TlsHandshaker.cpp
index 88f0062516..56fe011920 100644
--- a/netwerk/protocol/http/TlsHandshaker.cpp
+++ b/netwerk/protocol/http/TlsHandshaker.cpp
@@ -31,6 +31,24 @@ TlsHandshaker::TlsHandshaker(nsHttpConnectionInfo* aInfo,
TlsHandshaker::~TlsHandshaker() { LOG(("TlsHandshaker dtor %p", this)); }
NS_IMETHODIMP
+TlsHandshaker::CertVerificationDone() {
+ LOG(("TlsHandshaker::CertVerificationDone mOwner=%p", mOwner.get()));
+ if (mOwner) {
+ Unused << mOwner->ResumeSend();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TlsHandshaker::ClientAuthCertificateSelected() {
+ LOG(("TlsHandshaker::ClientAuthCertificateSelected mOwner=%p", mOwner.get()));
+ if (mOwner) {
+ Unused << mOwner->ResumeSend();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
TlsHandshaker::HandshakeDone() {
LOG(("TlsHandshaker::HandshakeDone mOwner=%p", mOwner.get()));
if (mOwner) {
diff --git a/netwerk/protocol/http/nsITlsHandshakeListener.idl b/netwerk/protocol/http/nsITlsHandshakeListener.idl
index cb5444f6d7..0d5806be24 100644
--- a/netwerk/protocol/http/nsITlsHandshakeListener.idl
+++ b/netwerk/protocol/http/nsITlsHandshakeListener.idl
@@ -9,4 +9,6 @@
[uuid(b4bbe824-ec4c-48be-9a40-6a7339347f40)]
interface nsITlsHandshakeCallbackListener : nsISupports {
[noscript] void handshakeDone();
+ [noscript] void certVerificationDone();
+ [noscript] void clientAuthCertificateSelected();
};
diff --git a/netwerk/test/unit/test_client_auth_with_proxy.js b/netwerk/test/unit/test_client_auth_with_proxy.js
new file mode 100644
index 0000000000..0584dba69d
--- /dev/null
+++ b/netwerk/test/unit/test_client_auth_with_proxy.js
@@ -0,0 +1,185 @@
+/* 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";
+
+/* import-globals-from head_cache.js */
+/* import-globals-from head_cookies.js */
+/* import-globals-from head_channels.js */
+/* import-globals-from head_servers.js */
+
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+const certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+].getService(Ci.nsICertOverrideService);
+
+function makeChan(uri) {
+ let chan = NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: true,
+ }).QueryInterface(Ci.nsIHttpChannel);
+ chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
+ return chan;
+}
+
+function channelOpenPromise(chan, flags) {
+ return new Promise(resolve => {
+ function finish(req, buffer) {
+ resolve([req, buffer]);
+ }
+ chan.asyncOpen(new ChannelListener(finish, null, flags));
+ });
+}
+
+class SecurityObserver {
+ constructor(input, output) {
+ this.input = input;
+ this.output = output;
+ }
+
+ onHandshakeDone(socket, status) {
+ info("TLS handshake done");
+
+ let output = this.output;
+ this.input.asyncWait(
+ {
+ onInputStreamReady(readyInput) {
+ let request = NetUtil.readInputStreamToString(
+ readyInput,
+ readyInput.available()
+ );
+ ok(
+ request.startsWith("GET /") && request.includes("HTTP/1.1"),
+ "expecting an HTTP/1.1 GET request"
+ );
+ let response =
+ "HTTP/1.1 200 OK\r\nContent-Type:text/plain\r\n" +
+ "Connection:Close\r\nContent-Length:2\r\n\r\nOK";
+ output.write(response, response.length);
+ },
+ },
+ 0,
+ 0,
+ Services.tm.currentThread
+ );
+ }
+}
+
+function startServer(cert) {
+ let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
+ Ci.nsITLSServerSocket
+ );
+ tlsServer.init(-1, true, -1);
+ tlsServer.serverCert = cert;
+
+ let securityObservers = [];
+
+ let listener = {
+ onSocketAccepted(socket, transport) {
+ info("Accepted TLS client connection");
+ let connectionInfo = transport.securityCallbacks.getInterface(
+ Ci.nsITLSServerConnectionInfo
+ );
+ let input = transport.openInputStream(0, 0, 0);
+ let output = transport.openOutputStream(0, 0, 0);
+ connectionInfo.setSecurityObserver(new SecurityObserver(input, output));
+ },
+
+ onStopListening() {
+ info("onStopListening");
+ for (let securityObserver of securityObservers) {
+ securityObserver.input.close();
+ securityObserver.output.close();
+ }
+ },
+ };
+
+ tlsServer.setSessionTickets(false);
+ tlsServer.setRequestClientCertificate(Ci.nsITLSServerSocket.REQUEST_ALWAYS);
+
+ tlsServer.asyncListen(listener);
+
+ return tlsServer;
+}
+
+// Replace the UI dialog that prompts the user to pick a client certificate.
+const gClientAuthDialogs = {
+ chooseCertificate(
+ hostname,
+ port,
+ organization,
+ issuerOrg,
+ certList,
+ selectedIndex,
+ rememberClientAuthCertificate
+ ) {
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIClientAuthDialogs"]),
+};
+
+let server;
+add_setup(async function setup() {
+ do_get_profile();
+
+ let clientAuthDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsClientAuthDialogs;1",
+ gClientAuthDialogs
+ );
+
+ let cert = getTestServerCertificate();
+ ok(!!cert, "Got self-signed cert");
+ server = startServer(cert);
+
+ certOverrideService.rememberValidityOverride(
+ "localhost",
+ server.port,
+ {},
+ cert,
+ true
+ );
+
+ registerCleanupFunction(async function () {
+ MockRegistrar.unregister(clientAuthDialogsCID);
+ certOverrideService.clearValidityOverride("localhost", server.port, {});
+ server.close();
+ });
+});
+
+add_task(async function test_client_auth_with_proxy() {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
+ addCertFromFile(certdb, "proxy-ca.pem", "CTu,u,u");
+
+ let proxies = [
+ NodeHTTPProxyServer,
+ NodeHTTPSProxyServer,
+ NodeHTTP2ProxyServer,
+ ];
+
+ for (let p of proxies) {
+ info(`Test with proxy:${p.name}`);
+ let proxy = new p();
+ await proxy.start();
+ registerCleanupFunction(async () => {
+ await proxy.stop();
+ });
+
+ let chan = makeChan(`https://localhost:${server.port}`);
+ let [req, buff] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
+ equal(req.status, Cr.NS_OK);
+ equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200);
+ equal(buff, "OK");
+ req.QueryInterface(Ci.nsIProxiedChannel);
+ ok(!!req.proxyInfo);
+ notEqual(req.proxyInfo.type, "direct");
+ await proxy.stop();
+ }
+});
diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini
index ed69ad2627..52433261bc 100644
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -775,3 +775,5 @@ skip-if =
os == 'win' && msix
true
run-sequentially = node server exceptions dont replay well
+[test_client_auth_with_proxy.js]
+skip-if = os == "android"