summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol')
-rw-r--r--netwerk/protocol/about/nsIAboutModule.idl1
-rw-r--r--netwerk/protocol/data/nsDataChannel.cpp5
-rw-r--r--netwerk/protocol/data/nsDataChannel.h4
-rw-r--r--netwerk/protocol/data/nsDataHandler.cpp13
-rw-r--r--netwerk/protocol/data/nsDataHandler.h3
-rw-r--r--netwerk/protocol/http/AlternateServices.cpp71
-rw-r--r--netwerk/protocol/http/AlternateServices.h6
-rw-r--r--netwerk/protocol/http/CacheControlParser.cpp5
-rw-r--r--netwerk/protocol/http/ConnectionEntry.cpp11
-rw-r--r--netwerk/protocol/http/ConnectionEntry.h7
-rw-r--r--netwerk/protocol/http/Http2Session.cpp23
-rw-r--r--netwerk/protocol/http/Http3Session.cpp52
-rw-r--r--netwerk/protocol/http/Http3StreamBase.h4
-rw-r--r--netwerk/protocol/http/Http3WebTransportStream.cpp4
-rw-r--r--netwerk/protocol/http/HttpBaseChannel.cpp6
-rw-r--r--netwerk/protocol/http/HttpChannelChild.cpp71
-rw-r--r--netwerk/protocol/http/WebTransportCertificateVerifier.cpp280
-rw-r--r--netwerk/protocol/http/WebTransportCertificateVerifier.h22
-rw-r--r--netwerk/protocol/http/moz.build1
-rw-r--r--netwerk/protocol/http/nsHttp.cpp12
-rw-r--r--netwerk/protocol/http/nsHttp.h4
-rw-r--r--netwerk/protocol/http/nsHttpChannel.cpp107
-rw-r--r--netwerk/protocol/http/nsHttpConnection.cpp2
-rw-r--r--netwerk/protocol/http/nsHttpConnectionInfo.cpp17
-rw-r--r--netwerk/protocol/http/nsHttpConnectionInfo.h6
-rw-r--r--netwerk/protocol/http/nsHttpConnectionMgr.cpp56
-rw-r--r--netwerk/protocol/http/nsHttpConnectionMgr.h16
-rw-r--r--netwerk/protocol/http/nsHttpHandler.cpp7
-rw-r--r--netwerk/protocol/http/nsHttpHandler.h14
-rw-r--r--netwerk/protocol/http/nsHttpTransaction.cpp18
-rw-r--r--netwerk/protocol/http/nsHttpTransaction.h1
-rw-r--r--netwerk/protocol/http/nsIHttpProtocolHandler.idl6
-rw-r--r--netwerk/protocol/res/PageThumbProtocolHandler.cpp2
-rw-r--r--netwerk/protocol/webtransport/WebTransportSessionProxy.cpp80
-rw-r--r--netwerk/protocol/webtransport/WebTransportSessionProxy.h6
-rw-r--r--netwerk/protocol/webtransport/nsIWebTransport.idl9
36 files changed, 755 insertions, 197 deletions
diff --git a/netwerk/protocol/about/nsIAboutModule.idl b/netwerk/protocol/about/nsIAboutModule.idl
index 7750dd2d4b..f35d9366e9 100644
--- a/netwerk/protocol/about/nsIAboutModule.idl
+++ b/netwerk/protocol/about/nsIAboutModule.idl
@@ -108,6 +108,5 @@ interface nsIAboutModule : nsISupports
#define NS_ABOUT_MODULE_CONTRACTID "@mozilla.org/network/protocol/about;1"
#define NS_ABOUT_MODULE_CONTRACTID_PREFIX NS_ABOUT_MODULE_CONTRACTID "?what="
-#define NS_ABOUT_MODULE_CONTRACTID_LENGTH 49 // strlen(NS_ABOUT_MODULE_CONTRACTID_PREFIX)
%}
diff --git a/netwerk/protocol/data/nsDataChannel.cpp b/netwerk/protocol/data/nsDataChannel.cpp
index 87b00adea3..df40971e1c 100644
--- a/netwerk/protocol/data/nsDataChannel.cpp
+++ b/netwerk/protocol/data/nsDataChannel.cpp
@@ -8,6 +8,7 @@
#include "nsDataChannel.h"
#include "mozilla/Base64.h"
+#include "mozilla/dom/MimeType.h"
#include "nsDataHandler.h"
#include "nsIInputStream.h"
#include "nsEscape.h"
@@ -59,9 +60,10 @@ nsresult nsDataChannel::OpenContentStream(bool async, nsIInputStream** result,
nsCString contentType, contentCharset;
nsDependentCSubstring dataRange;
+ RefPtr<CMimeType> fullMimeType;
bool lBase64;
rv = nsDataHandler::ParsePathWithoutRef(path, contentType, &contentCharset,
- lBase64, &dataRange, &mMimeType);
+ lBase64, &dataRange, &fullMimeType);
if (NS_FAILED(rv)) return rv;
// This will avoid a copy if nothing needs to be unescaped.
@@ -103,6 +105,7 @@ nsresult nsDataChannel::OpenContentStream(bool async, nsIInputStream** result,
SetContentType(contentType);
SetContentCharset(contentCharset);
+ SetFullMimeType(std::move(fullMimeType));
mContentLength = contentLen;
// notify "data-channel-opened" observers
diff --git a/netwerk/protocol/data/nsDataChannel.h b/netwerk/protocol/data/nsDataChannel.h
index d7313d66a0..f7700ff2b4 100644
--- a/netwerk/protocol/data/nsDataChannel.h
+++ b/netwerk/protocol/data/nsDataChannel.h
@@ -16,14 +16,10 @@ class nsDataChannel : public nsBaseChannel {
public:
explicit nsDataChannel(nsIURI* uri) { SetURI(uri); }
- const nsACString& MimeType() const { return mMimeType; }
-
protected:
[[nodiscard]] virtual nsresult OpenContentStream(
bool async, nsIInputStream** result, nsIChannel** channel) override;
- nsCString mMimeType;
-
private:
nsresult MaybeSendDataChannelOpenNotification();
};
diff --git a/netwerk/protocol/data/nsDataHandler.cpp b/netwerk/protocol/data/nsDataHandler.cpp
index d3a5743097..adb86bf484 100644
--- a/netwerk/protocol/data/nsDataHandler.cpp
+++ b/netwerk/protocol/data/nsDataHandler.cpp
@@ -161,7 +161,7 @@ nsresult nsDataHandler::ParsePathWithoutRef(const nsACString& aPath,
nsCString* aContentCharset,
bool& aIsBase64,
nsDependentCSubstring* aDataBuffer,
- nsCString* aMimeType) {
+ RefPtr<CMimeType>* aMimeType) {
static constexpr auto kCharset = "charset"_ns;
// This implements https://fetch.spec.whatwg.org/#data-url-processor
@@ -200,18 +200,18 @@ nsresult nsDataHandler::ParsePathWithoutRef(const nsACString& aPath,
// This also checks for instances of ;base64 in the middle of the MimeType.
// This is against the current spec, but we're doing it because we have
// historically seen webcompat issues relying on this (see bug 781693).
- if (mozilla::UniquePtr<CMimeType> parsed = CMimeType::Parse(mimeType)) {
+ if (RefPtr<CMimeType> parsed = CMimeType::Parse(mimeType)) {
parsed->GetEssence(aContentType);
if (aContentCharset) {
parsed->GetParameterValue(kCharset, *aContentCharset);
}
- if (aMimeType) {
- parsed->Serialize(*aMimeType);
- }
if (parsed->IsBase64() &&
!StaticPrefs::network_url_strict_data_url_base64_placement()) {
aIsBase64 = true;
}
+ if (aMimeType) {
+ *aMimeType = std::move(parsed);
+ }
} else {
// "If mimeTypeRecord is failure, then set mimeTypeRecord to
// text/plain;charset=US-ASCII."
@@ -220,7 +220,8 @@ nsresult nsDataHandler::ParsePathWithoutRef(const nsACString& aPath,
aContentCharset->AssignLiteral("US-ASCII");
}
if (aMimeType) {
- aMimeType->AssignLiteral("text/plain;charset=US-ASCII");
+ *aMimeType = new CMimeType("text"_ns, "plain"_ns);
+ (*aMimeType)->SetParameterValue("charset"_ns, "US-ASCII"_ns);
}
}
diff --git a/netwerk/protocol/data/nsDataHandler.h b/netwerk/protocol/data/nsDataHandler.h
index 4796f0f453..499e73b220 100644
--- a/netwerk/protocol/data/nsDataHandler.h
+++ b/netwerk/protocol/data/nsDataHandler.h
@@ -6,6 +6,7 @@
#ifndef nsDataHandler_h___
#define nsDataHandler_h___
+#include "mozilla/dom/MimeType.h"
#include "nsIProtocolHandler.h"
#include "nsWeakReference.h"
@@ -50,7 +51,7 @@ class nsDataHandler : public nsIProtocolHandler,
[[nodiscard]] static nsresult ParsePathWithoutRef(
const nsACString& aPath, nsCString& aContentType,
nsCString* aContentCharset, bool& aIsBase64,
- nsDependentCSubstring* aDataBuffer, nsCString* aMimeType = nullptr);
+ nsDependentCSubstring* aDataBuffer, RefPtr<CMimeType>* = nullptr);
};
#endif /* nsDataHandler_h___ */
diff --git a/netwerk/protocol/http/AlternateServices.cpp b/netwerk/protocol/http/AlternateServices.cpp
index a386206b6d..e0571f02ce 100644
--- a/netwerk/protocol/http/AlternateServices.cpp
+++ b/netwerk/protocol/http/AlternateServices.cpp
@@ -7,6 +7,7 @@
#include "HttpLog.h"
#include "AlternateServices.h"
+#include <algorithm>
#include "LoadInfo.h"
#include "mozilla/Atomics.h"
#include "mozilla/StaticPrefs_network.h"
@@ -87,16 +88,15 @@ void AltSvcMapping::ProcessHeader(
ParsedHeaderValueListList parsedAltSvc(buf);
int32_t numEntriesInHeader = parsedAltSvc.mValues.Length();
- // Only use one http3 version.
- bool http3Found = false;
-
+ nsTArray<RefPtr<AltSvcMapping>> h3Mappings;
+ nsTArray<RefPtr<AltSvcMapping>> otherMappings;
for (uint32_t index = 0; index < parsedAltSvc.mValues.Length(); ++index) {
uint32_t maxage = 86400; // default
nsAutoCString hostname;
nsAutoCString npnToken;
int32_t portno = originPort;
bool clearEntry = false;
- bool isHttp3 = false;
+ SupportedAlpnRank alpnRank = SupportedAlpnRank::NOT_SUPPORTED;
for (uint32_t pairIndex = 0;
pairIndex < parsedAltSvc.mValues[index].mValues.Length();
@@ -116,7 +116,7 @@ void AltSvcMapping::ProcessHeader(
// h2=[hostname]:443 or h3-xx=[hostname]:port
// XX is current version we support and it is define in nsHttp.h.
- isHttp3 = gHttpHandler->IsHttp3VersionSupported(currentName);
+ alpnRank = IsAlpnSupported(currentName);
npnToken = currentName;
int32_t colonIndex = currentValue.FindChar(':');
@@ -153,12 +153,7 @@ void AltSvcMapping::ProcessHeader(
// update nsCString length
nsUnescape(npnToken.BeginWriting());
npnToken.SetLength(strlen(npnToken.BeginReading()));
-
- if (http3Found && isHttp3) {
- LOG(("Alt Svc ignore multiple Http3 options (%s)", npnToken.get()));
- continue;
- }
-
+ bool isHttp3 = net::IsHttp3(alpnRank);
SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo();
if (!(npnToken.Equals(spdyInfo->VersionString) &&
StaticPrefs::network_http_http2_enabled()) &&
@@ -169,16 +164,13 @@ void AltSvcMapping::ProcessHeader(
continue;
}
- if (isHttp3) {
- http3Found = true;
- }
-
- RefPtr<AltSvcMapping> mapping =
- new AltSvcMapping(gHttpHandler->AltServiceCache()->GetStoragePtr(),
- gHttpHandler->AltServiceCache()->StorageEpoch(),
- originScheme, originHost, originPort, username,
- privateBrowsing, NowInSeconds() + maxage, hostname,
- portno, npnToken, originAttributes, isHttp3);
+ LOG(("AltSvcMapping created npnToken=%s", npnToken.get()));
+ RefPtr<AltSvcMapping> mapping = new AltSvcMapping(
+ gHttpHandler->AltServiceCache()->GetStoragePtr(),
+ gHttpHandler->AltServiceCache()->StorageEpoch(), originScheme,
+ originHost, originPort, username, privateBrowsing,
+ NowInSeconds() + maxage, hostname, portno, npnToken, originAttributes,
+ isHttp3, alpnRank);
if (mapping->TTL() <= 0) {
LOG(("Alt Svc invalid map"));
mapping = nullptr;
@@ -186,15 +178,39 @@ void AltSvcMapping::ProcessHeader(
// as that would have happened if we had accepted the parameters.
gHttpHandler->AltServiceCache()->ClearHostMapping(originHost, originPort,
originAttributes);
- } else if (!aDontValidate) {
- gHttpHandler->UpdateAltServiceMapping(mapping, proxyInfo, callbacks, caps,
- originAttributes);
+ } else {
+ if (isHttp3) {
+ h3Mappings.AppendElement(std::move(mapping));
+ } else {
+ otherMappings.AppendElement(std::move(mapping));
+ }
+ }
+ }
+
+ auto doUpdateAltSvcMapping = [&](AltSvcMapping* aMapping) {
+ if (!aDontValidate) {
+ gHttpHandler->UpdateAltServiceMapping(aMapping, proxyInfo, callbacks,
+ caps, originAttributes);
} else {
gHttpHandler->UpdateAltServiceMappingWithoutValidation(
- mapping, proxyInfo, callbacks, caps, originAttributes);
+ aMapping, proxyInfo, callbacks, caps, originAttributes);
}
+ };
+
+ if (!h3Mappings.IsEmpty()) {
+ // Select the HTTP/3 (h3) AltSvcMapping with the highest ALPN rank from
+ // h3Mappings.
+ RefPtr<AltSvcMapping> latestH3Mapping = *std::max_element(
+ h3Mappings.begin(), h3Mappings.end(),
+ [](const RefPtr<AltSvcMapping>& a, const RefPtr<AltSvcMapping>& b) {
+ return a->AlpnRank() < b->AlpnRank();
+ });
+ doUpdateAltSvcMapping(latestH3Mapping);
}
+ std::for_each(otherMappings.begin(), otherMappings.end(),
+ doUpdateAltSvcMapping);
+
if (numEntriesInHeader) { // Ignore headers that were just "alt-svc: clear"
Telemetry::Accumulate(Telemetry::HTTP_ALTSVC_ENTRIES_PER_HEADER,
numEntriesInHeader);
@@ -209,7 +225,7 @@ AltSvcMapping::AltSvcMapping(nsIDataStorage* storage, int32_t epoch,
const nsACString& alternateHost,
int32_t alternatePort, const nsACString& npnToken,
const OriginAttributes& originAttributes,
- bool aIsHttp3)
+ bool aIsHttp3, SupportedAlpnRank aRank)
: mStorage(storage),
mStorageEpoch(epoch),
mAlternateHost(alternateHost),
@@ -221,7 +237,8 @@ AltSvcMapping::AltSvcMapping(nsIDataStorage* storage, int32_t epoch,
mExpiresAt(expiresAt),
mNPNToken(npnToken),
mOriginAttributes(originAttributes),
- mIsHttp3(aIsHttp3) {
+ mIsHttp3(aIsHttp3),
+ mAlpnRank(aRank) {
MOZ_ASSERT(NS_IsMainThread());
if (NS_FAILED(SchemeIsHTTPS(originScheme, mHttps))) {
diff --git a/netwerk/protocol/http/AlternateServices.h b/netwerk/protocol/http/AlternateServices.h
index 4de5a281c0..02b45f7bee 100644
--- a/netwerk/protocol/http/AlternateServices.h
+++ b/netwerk/protocol/http/AlternateServices.h
@@ -23,6 +23,7 @@ https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06
#ifndef mozilla_net_AlternateServices_h
#define mozilla_net_AlternateServices_h
+#include "nsHttp.h"
#include "nsRefPtrHashtable.h"
#include "nsString.h"
#include "nsIDataStorage.h"
@@ -53,7 +54,8 @@ class AltSvcMapping {
bool privateBrowsing, uint32_t expiresAt,
const nsACString& alternateHost, int32_t alternatePort,
const nsACString& npnToken,
- const OriginAttributes& originAttributes, bool aIsHttp3);
+ const OriginAttributes& originAttributes, bool aIsHttp3,
+ SupportedAlpnRank aRank);
public:
AltSvcMapping(nsIDataStorage* storage, int32_t storageEpoch,
@@ -103,6 +105,7 @@ class AltSvcMapping {
bool IsHttp3() { return mIsHttp3; }
const nsCString& NPNToken() const { return mNPNToken; }
+ SupportedAlpnRank AlpnRank() const { return mAlpnRank; }
private:
virtual ~AltSvcMapping() = default;
@@ -138,6 +141,7 @@ class AltSvcMapping {
bool mSyncOnlyOnSuccess{false};
bool mIsHttp3{false};
+ SupportedAlpnRank mAlpnRank{SupportedAlpnRank::NOT_SUPPORTED};
};
class AltSvcOverride : public nsIInterfaceRequestor,
diff --git a/netwerk/protocol/http/CacheControlParser.cpp b/netwerk/protocol/http/CacheControlParser.cpp
index 79cdef5439..4166ab783d 100644
--- a/netwerk/protocol/http/CacheControlParser.cpp
+++ b/netwerk/protocol/http/CacheControlParser.cpp
@@ -69,6 +69,7 @@ void CacheControlParser::Directive() {
bool CacheControlParser::SecondsValue(uint32_t* seconds, uint32_t defaultVal) {
SkipWhites();
if (!CheckChar('=')) {
+ IgnoreDirective();
*seconds = defaultVal;
return !!defaultVal;
}
@@ -76,7 +77,9 @@ bool CacheControlParser::SecondsValue(uint32_t* seconds, uint32_t defaultVal) {
SkipWhites();
if (!ReadInteger(seconds)) {
NS_WARNING("Unexpected value in Cache-control header value");
- return false;
+ IgnoreDirective();
+ *seconds = defaultVal;
+ return !!defaultVal;
}
return true;
diff --git a/netwerk/protocol/http/ConnectionEntry.cpp b/netwerk/protocol/http/ConnectionEntry.cpp
index 473f0ab9fc..7b59011cd3 100644
--- a/netwerk/protocol/http/ConnectionEntry.cpp
+++ b/netwerk/protocol/http/ConnectionEntry.cpp
@@ -1098,5 +1098,16 @@ void ConnectionEntry::SetRetryDifferentIPFamilyForHttp3(uint16_t aIPFamily) {
MOZ_DIAGNOSTIC_ASSERT(mPreferIPv4 ^ mPreferIPv6);
}
+void ConnectionEntry::SetServerCertHashes(
+ nsTArray<RefPtr<nsIWebTransportHash>>&& aHashes) {
+ mServerCertHashes = std::move(aHashes);
+}
+
+const nsTArray<RefPtr<nsIWebTransportHash>>&
+ConnectionEntry::GetServerCertHashes() {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ return mServerCertHashes;
+}
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/protocol/http/ConnectionEntry.h b/netwerk/protocol/http/ConnectionEntry.h
index 811c5481de..8ccc126503 100644
--- a/netwerk/protocol/http/ConnectionEntry.h
+++ b/netwerk/protocol/http/ConnectionEntry.h
@@ -200,6 +200,10 @@ class ConnectionEntry {
bool AllowToRetryDifferentIPFamilyForHttp3(nsresult aError);
void SetRetryDifferentIPFamilyForHttp3(uint16_t aIPFamily);
+ void SetServerCertHashes(nsTArray<RefPtr<nsIWebTransportHash>>&& aHashes);
+
+ const nsTArray<RefPtr<nsIWebTransportHash>>& GetServerCertHashes();
+
private:
void InsertIntoIdleConnections_internal(nsHttpConnection* conn);
void RemoveFromIdleConnectionsIndex(size_t inx);
@@ -219,6 +223,9 @@ class ConnectionEntry {
nsTArray<RefPtr<DnsAndConnectSocket>>
mDnsAndConnectSockets; // dns resolution and half open connections
+ // If serverCertificateHashes are used, these are stored here
+ nsTArray<RefPtr<nsIWebTransportHash>> mServerCertHashes;
+
PendingTransactionQueue mPendingQ;
~ConnectionEntry();
diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp
index e969d60c4d..d5793b2147 100644
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -223,7 +223,7 @@ void Http2Session::ShutdownStream(Http2StreamBase* aStream, nsresult aReason) {
CloseStream(aStream, NS_ERROR_NET_INADEQUATE_SECURITY);
} else if (!mCleanShutdown && (mGoAwayReason != NO_HTTP_ERROR)) {
CloseStream(aStream, NS_ERROR_NET_HTTP2_SENT_GOAWAY);
- } else if (!mCleanShutdown && SecurityErrorThatMayNeedRestart(aReason)) {
+ } else if (!mCleanShutdown && PossibleZeroRTTRetryError(aReason)) {
CloseStream(aStream, aReason);
} else {
CloseStream(aStream, NS_ERROR_ABORT);
@@ -1419,6 +1419,13 @@ nsresult Http2Session::RecvHeaders(Http2Session* self) {
return self->SessionError(PROTOCOL_ERROR);
}
+ uint32_t frameSize = self->mInputFrameDataSize - paddingControlBytes -
+ priorityLen - paddingLength;
+ if (self->mAggregatedHeaderSize + frameSize >
+ StaticPrefs::network_http_max_response_header_size()) {
+ LOG(("Http2Session %p header exceeds the limit\n", self));
+ return self->SessionError(PROTOCOL_ERROR);
+ }
if (!self->mInputFrameDataStream) {
// Cannot find stream. We can continue the session, but we need to
// uncompress the header block to maintain the correct compression context
@@ -1435,8 +1442,7 @@ nsresult Http2Session::RecvHeaders(Http2Session* self) {
self->mDecompressBuffer.Append(
&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes +
priorityLen],
- self->mInputFrameDataSize - paddingControlBytes - priorityLen -
- paddingLength);
+ frameSize);
if (self->mInputFrameFlags & kFlag_END_HEADERS) {
rv = self->UncompressAndDiscard(false);
@@ -1466,21 +1472,16 @@ nsresult Http2Session::RecvHeaders(Http2Session* self) {
self->mDecompressBuffer.Append(
&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes +
priorityLen],
- self->mInputFrameDataSize - paddingControlBytes - priorityLen -
- paddingLength);
+ frameSize);
self->mInputFrameDataStream->UpdateTransportReadEvents(
self->mInputFrameDataSize);
self->mLastDataReadEpoch = self->mLastReadEpoch;
if (!isContinuation) {
- self->mAggregatedHeaderSize = self->mInputFrameDataSize -
- paddingControlBytes - priorityLen -
- paddingLength;
+ self->mAggregatedHeaderSize = frameSize;
} else {
- self->mAggregatedHeaderSize += self->mInputFrameDataSize -
- paddingControlBytes - priorityLen -
- paddingLength;
+ self->mAggregatedHeaderSize += frameSize;
}
if (!endHeadersFlag) { // more are coming - don't process yet
diff --git a/netwerk/protocol/http/Http3Session.cpp b/netwerk/protocol/http/Http3Session.cpp
index 0369da94ac..80ee3267fa 100644
--- a/netwerk/protocol/http/Http3Session.cpp
+++ b/netwerk/protocol/http/Http3Session.cpp
@@ -29,6 +29,7 @@
#include "nsSocketTransportService2.h"
#include "nsThreadUtils.h"
#include "sslerr.h"
+#include "WebTransportCertificateVerifier.h"
namespace mozilla::net {
@@ -153,7 +154,20 @@ nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo,
SessionCacheInfo info;
udpConn->ChangeConnectionState(ConnectionState::TLS_HANDSHAKING);
- if (StaticPrefs::network_http_http3_enable_0rtt() &&
+ auto hasServCertHashes = [&]() -> bool {
+ if (!mConnInfo->GetWebTransport()) {
+ return false;
+ }
+ const nsTArray<RefPtr<nsIWebTransportHash>>* servCertHashes =
+ gHttpHandler->ConnMgr()->GetServerCertHashes(mConnInfo);
+ return servCertHashes && !servCertHashes->IsEmpty();
+ };
+
+ // In WebTransport, when servCertHashes is specified, it indicates that the
+ // connection to the WebTransport server should authenticate using the
+ // expected certificate hash. Therefore, 0RTT should be disabled in this
+ // context to ensure the certificate hash is checked.
+ if (StaticPrefs::network_http_http3_enable_0rtt() && !hasServCertHashes() &&
NS_SUCCEEDED(SSLTokensCache::Get(peerId, token, info))) {
LOG(("Found a resumption token in the cache."));
mHttp3Connection->SetResumptionToken(token);
@@ -1514,7 +1528,7 @@ nsresult Http3Session::SendData(nsIUDPSocket* socket) {
while (CanSendData() && (stream = mReadyForWrite.PopFront())) {
LOG(("Http3Session::SendData call ReadSegments from stream=%p [this=%p]",
stream.get(), this));
-
+ stream->SetInTxQueue(false);
rv = stream->ReadSegments();
// on stream error we return earlier to let the error be handled.
@@ -1557,7 +1571,15 @@ nsresult Http3Session::SendData(nsIUDPSocket* socket) {
void Http3Session::StreamReadyToWrite(Http3StreamBase* aStream) {
MOZ_ASSERT(aStream);
+ // Http3Session::StreamReadyToWrite can be called multiple times when we get
+ // duplicate DataWrite events from neqo at the same time. In this case, we
+ // only want to insert the stream in `mReadyForWrite` once.
+ if (aStream->IsInTxQueue()) {
+ return;
+ }
+
mReadyForWrite.Push(aStream);
+ aStream->SetInTxQueue(true);
if (CanSendData() && mConnection) {
Unused << mConnection->ResumeSend();
}
@@ -2119,6 +2141,32 @@ void Http3Session::CallCertVerification(Maybe<nsCString> aEchPublicName) {
return;
}
+ if (mConnInfo->GetWebTransport()) {
+ // if our connection is webtransport, we might do a verification
+ // based on serverCertificatedHashes
+ const nsTArray<RefPtr<nsIWebTransportHash>>* servCertHashes =
+ gHttpHandler->ConnMgr()->GetServerCertHashes(mConnInfo);
+ if (servCertHashes && !servCertHashes->IsEmpty() &&
+ certInfo.certs.Length() >= 1) {
+ // ok, we verify based on serverCertificateHashes
+ mozilla::pkix::Result rv = AuthCertificateWithServerCertificateHashes(
+ certInfo.certs[0], *servCertHashes);
+ if (rv != mozilla::pkix::Result::Success) {
+ // ok we failed, report it back
+ LOG(
+ ("Http3Session::CallCertVerification [this=%p] "
+ "AuthCertificateWithServerCertificateHashes failed",
+ this));
+ mHttp3Connection->PeerAuthenticated(SSL_ERROR_BAD_CERTIFICATE);
+ mError = psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERTIFICATE);
+ return;
+ }
+ // ok, we succeded
+ Authenticated(0);
+ return;
+ }
+ }
+
Maybe<nsTArray<nsTArray<uint8_t>>> stapledOCSPResponse;
if (certInfo.stapled_ocsp_responses_present) {
stapledOCSPResponse.emplace(std::move(certInfo.stapled_ocsp_responses));
diff --git a/netwerk/protocol/http/Http3StreamBase.h b/netwerk/protocol/http/Http3StreamBase.h
index 9ca85de98e..d658741384 100644
--- a/netwerk/protocol/http/Http3StreamBase.h
+++ b/netwerk/protocol/http/Http3StreamBase.h
@@ -52,6 +52,9 @@ class Http3StreamBase : public SupportsWeakPtr, public ARefBase {
virtual bool RecvdReset() const { return mResetRecv; }
virtual void SetRecvdReset() { mResetRecv = true; }
+ void SetInTxQueue(bool aValue) { mInTxQueue = aValue; }
+ bool IsInTxQueue() const { return mInTxQueue; }
+
protected:
~Http3StreamBase();
@@ -63,6 +66,7 @@ class Http3StreamBase : public SupportsWeakPtr, public ARefBase {
bool mQueued{false};
bool mFin{false};
bool mResetRecv{false};
+ bool mInTxQueue{false};
};
} // namespace mozilla::net
diff --git a/netwerk/protocol/http/Http3WebTransportStream.cpp b/netwerk/protocol/http/Http3WebTransportStream.cpp
index c76d8ef798..01a467030f 100644
--- a/netwerk/protocol/http/Http3WebTransportStream.cpp
+++ b/netwerk/protocol/http/Http3WebTransportStream.cpp
@@ -527,6 +527,10 @@ nsresult Http3WebTransportStream::WriteSegments() {
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
rv = NS_OK;
}
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ mReceiveStreamPipeOut->Close();
+ rv = NS_OK;
+ }
again = false;
} else if (NS_FAILED(mSocketInCondition)) {
if (mSocketInCondition != NS_BASE_STREAM_WOULD_BLOCK) {
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
index 59242f6933..9008d758fc 100644
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3927,7 +3927,6 @@ HttpBaseChannel::GetRequestMode(RequestMode* aMode) {
NS_IMETHODIMP
HttpBaseChannel::SetRequestMode(RequestMode aMode) {
- MOZ_ASSERT(aMode != RequestMode::EndGuard_);
mRequestMode = aMode;
return NS_OK;
}
@@ -4438,6 +4437,9 @@ void HttpBaseChannel::DoNotifyListener() {
// as not-pending.
StoreIsPending(false);
+ // notify "http-on-before-stop-request" observers
+ gHttpHandler->OnBeforeStopRequest(this);
+
if (mListener && !LoadOnStopRequestCalled()) {
nsCOMPtr<nsIStreamListener> listener = mListener;
StoreOnStopRequestCalled(true);
@@ -4445,7 +4447,7 @@ void HttpBaseChannel::DoNotifyListener() {
}
StoreOnStopRequestCalled(true);
- // notify "http-on-stop-connect" observers
+ // notify "http-on-stop-request" observers
gHttpHandler->OnStopRequest(this);
// This channel has finished its job, potentially release any tail-blocked
diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
index 393d0aa37d..c7009f34d3 100644
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1160,25 +1160,45 @@ void HttpChannelChild::CollectOMTTelemetry() {
key, static_cast<LABELS_HTTP_CHILD_OMT_STATS>(mOMTResult));
}
+// We want to inspect all upgradable mixed content loads
+// (i.e., loads point to HTTP from an HTTPS page), for
+// resources that stem from audio, video and img elements.
+// Of those, we want to measure which succceed and which fail.
+// Some double negatives, but we check the following:exempt loads that
+// 1) Request was upgraded as mixed passive content
+// 2) Request _could_ have been upgraded as mixed passive content if the pref
+// had been set and Request wasn't upgraded by any other means (URL isn't https)
void HttpChannelChild::CollectMixedContentTelemetry() {
MOZ_ASSERT(NS_IsMainThread());
- nsContentPolicyType internalLoadType;
- mLoadInfo->GetInternalContentPolicyType(&internalLoadType);
- bool statusIsSuccess = NS_SUCCEEDED(mStatus);
+ bool wasUpgraded = mLoadInfo->GetBrowserDidUpgradeInsecureRequests();
+ if (!wasUpgraded) {
+ // If this wasn't upgraded, let's check if it _could_ have been upgraded as
+ // passive mixed content and that it wasn't upgraded with any other method
+ if (!mURI->SchemeIs("https") &&
+ !mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
+ return;
+ }
+ }
+
+ // UseCounters require a document.
RefPtr<Document> doc;
mLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
if (!doc) {
return;
}
- if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE ||
- internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD) {
- if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
+
+ nsContentPolicyType internalLoadType;
+ mLoadInfo->GetInternalContentPolicyType(&internalLoadType);
+ bool statusIsSuccess = NS_SUCCEEDED(mStatus);
+
+ if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE) {
+ if (wasUpgraded) {
doc->SetUseCounter(
statusIsSuccess
? eUseCounter_custom_MixedContentUpgradedImageSuccess
: eUseCounter_custom_MixedContentUpgradedImageFailure);
- } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
+ } else {
doc->SetUseCounter(
statusIsSuccess
? eUseCounter_custom_MixedContentNotUpgradedImageSuccess
@@ -1187,12 +1207,12 @@ void HttpChannelChild::CollectMixedContentTelemetry() {
return;
}
if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_VIDEO) {
- if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
+ if (wasUpgraded) {
doc->SetUseCounter(
statusIsSuccess
? eUseCounter_custom_MixedContentUpgradedVideoSuccess
: eUseCounter_custom_MixedContentUpgradedVideoFailure);
- } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
+ } else {
doc->SetUseCounter(
statusIsSuccess
? eUseCounter_custom_MixedContentNotUpgradedVideoSuccess
@@ -1201,12 +1221,12 @@ void HttpChannelChild::CollectMixedContentTelemetry() {
return;
}
if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_AUDIO) {
- if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
+ if (wasUpgraded) {
doc->SetUseCounter(
statusIsSuccess
? eUseCounter_custom_MixedContentUpgradedAudioSuccess
: eUseCounter_custom_MixedContentUpgradedAudioFailure);
- } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
+ } else {
doc->SetUseCounter(
statusIsSuccess
? eUseCounter_custom_MixedContentNotUpgradedAudioSuccess
@@ -1262,6 +1282,9 @@ void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
MOZ_ASSERT(!LoadOnStopRequestCalled(),
"We should not call OnStopRequest twice");
+ // notify "http-on-before-stop-request" observers
+ gHttpHandler->OnBeforeStopRequest(this);
+
if (mListener) {
nsCOMPtr<nsIStreamListener> listener(mListener);
StoreOnStopRequestCalled(true);
@@ -1280,7 +1303,7 @@ void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
return;
}
- // notify "http-on-stop-connect" observers
+ // notify "http-on-stop-request" observers
gHttpHandler->OnStopRequest(this);
ReleaseListeners();
@@ -1471,6 +1494,9 @@ void HttpChannelChild::ContinueDoNotifyListener() {
// as not-pending.
StoreIsPending(false);
+ // notify "http-on-before-stop-request" observers
+ gHttpHandler->OnBeforeStopRequest(this);
+
if (mListener && !LoadOnStopRequestCalled()) {
nsCOMPtr<nsIStreamListener> listener = mListener;
StoreOnStopRequestCalled(true);
@@ -3083,30 +3109,19 @@ HttpChannelChild::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
void HttpChannelChild::TrySendDeletingChannel() {
AUTO_PROFILER_LABEL("HttpChannelChild::TrySendDeletingChannel", NETWORK);
+ MOZ_ASSERT(NS_IsMainThread());
+
if (!mDeletingChannelSent.compareExchange(false, true)) {
// SendDeletingChannel is already sent.
return;
}
- if (NS_IsMainThread()) {
- if (NS_WARN_IF(!CanSend())) {
- // IPC actor is destroyed already, do not send more messages.
- return;
- }
-
- Unused << PHttpChannelChild::SendDeletingChannel();
+ if (NS_WARN_IF(!CanSend())) {
+ // IPC actor is destroyed already, do not send more messages.
return;
}
- nsCOMPtr<nsISerialEventTarget> neckoTarget = GetNeckoTarget();
- MOZ_ASSERT(neckoTarget);
-
- DebugOnly<nsresult> rv = neckoTarget->Dispatch(
- NewNonOwningRunnableMethod(
- "net::HttpChannelChild::TrySendDeletingChannel", this,
- &HttpChannelChild::TrySendDeletingChannel),
- NS_DISPATCH_NORMAL);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
+ Unused << PHttpChannelChild::SendDeletingChannel();
}
nsresult HttpChannelChild::AsyncCallImpl(
diff --git a/netwerk/protocol/http/WebTransportCertificateVerifier.cpp b/netwerk/protocol/http/WebTransportCertificateVerifier.cpp
new file mode 100644
index 0000000000..cc778640a1
--- /dev/null
+++ b/netwerk/protocol/http/WebTransportCertificateVerifier.cpp
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "WebTransportCertificateVerifier.h"
+#include "ScopedNSSTypes.h"
+#include "nss/mozpkix/pkixutil.h"
+#include "nss/mozpkix/pkixcheck.h"
+#include "hasht.h"
+
+namespace mozilla::psm {
+
+class ServerCertHashesTrustDomain : public mozilla::pkix::TrustDomain {
+ public:
+ ServerCertHashesTrustDomain() = default;
+
+ virtual mozilla::pkix::Result FindIssuer(
+ mozilla::pkix::Input encodedIssuerName, IssuerChecker& checker,
+ mozilla::pkix::Time time) override;
+
+ virtual mozilla::pkix::Result GetCertTrust(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertPolicyId& policy,
+ mozilla::pkix::Input candidateCertDER,
+ /*out*/ mozilla::pkix::TrustLevel& trustLevel) override;
+
+ virtual mozilla::pkix::Result CheckSignatureDigestAlgorithm(
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::Time notBefore) override;
+
+ virtual mozilla::pkix::Result CheckRSAPublicKeyModulusSizeInBits(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ unsigned int modulusSizeInBits) override;
+
+ virtual mozilla::pkix::Result VerifyRSAPKCS1SignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual mozilla::pkix::Result VerifyRSAPSSSignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual mozilla::pkix::Result CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) override;
+
+ virtual mozilla::pkix::Result VerifyECDSASignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual mozilla::pkix::Result DigestBuf(
+ mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen) override;
+
+ virtual mozilla::pkix::Result CheckValidityIsAcceptable(
+ mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::KeyPurposeId keyPurpose) override;
+
+ virtual mozilla::pkix::Result NetscapeStepUpMatchesServerAuth(
+ mozilla::pkix::Time notBefore,
+ /*out*/ bool& matches) override;
+
+ virtual mozilla::pkix::Result CheckRevocation(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
+ mozilla::pkix::Duration validityDuration,
+ /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+ /*optional*/ const mozilla::pkix::Input* aiaExtension,
+ /*optional*/ const mozilla::pkix::Input* sctExtension) override;
+
+ virtual mozilla::pkix::Result IsChainValid(
+ const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time,
+ const mozilla::pkix::CertPolicyId& requiredPolicy) override;
+
+ virtual void NoteAuxiliaryExtension(
+ mozilla::pkix::AuxiliaryExtension extension,
+ mozilla::pkix::Input extensionData) override;
+};
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::FindIssuer(
+ mozilla::pkix::Input encodedIssuerName, IssuerChecker& checker,
+ mozilla::pkix::Time time) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::GetCertTrust(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertPolicyId& policy,
+ mozilla::pkix::Input candidateCertDER,
+ /*out*/ mozilla::pkix::TrustLevel& trustLevel) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result
+ServerCertHashesTrustDomain::CheckSignatureDigestAlgorithm(
+ mozilla::pkix::DigestAlgorithm digestAlg,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA, mozilla::pkix::Time notBefore) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result
+ServerCertHashesTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ unsigned int modulusSizeInBits) {
+ return mozilla::pkix::Result::
+ ERROR_UNSUPPORTED_KEYALG; // RSA is not supported for
+ // serverCertificateHashes,
+ // Chromium does only support it for an intermediate period due to spec
+ // change, we do not support it.
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyRSAPKCS1SignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyRSAPSSSignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) {
+ return mozilla::pkix::Result::Success;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::VerifyECDSASignedData(
+ mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm,
+ mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::DigestBuf(
+ mozilla::pkix::Input item, mozilla::pkix::DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf, size_t digestBufLen) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::CheckValidityIsAcceptable(
+ mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::KeyPurposeId keyPurpose) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result
+ServerCertHashesTrustDomain::NetscapeStepUpMatchesServerAuth(
+ mozilla::pkix::Time notBefore,
+ /*out*/ bool& matches) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::CheckRevocation(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
+ mozilla::pkix::Duration validityDuration,
+ /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+ /*optional*/ const mozilla::pkix::Input* aiaExtension,
+ /*optional*/ const mozilla::pkix::Input* sctExtension) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+mozilla::pkix::Result ServerCertHashesTrustDomain::IsChainValid(
+ const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time,
+ const mozilla::pkix::CertPolicyId& requiredPolicy) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+
+ return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+void ServerCertHashesTrustDomain::NoteAuxiliaryExtension(
+ mozilla::pkix::AuxiliaryExtension extension,
+ mozilla::pkix::Input extensionData) {
+ MOZ_ASSERT_UNREACHABLE("not expecting this to be called");
+}
+
+} // namespace mozilla::psm
+
+namespace mozilla::net {
+// Does certificate verificate as required for serverCertificateHashes
+// This function is currently only used for Quic, but may be used later also for
+// http/2
+mozilla::pkix::Result AuthCertificateWithServerCertificateHashes(
+ nsTArray<uint8_t>& peerCert,
+ const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes) {
+ using namespace mozilla::pkix;
+ Input certDER;
+ mozilla::pkix::Result rv =
+ certDER.Init(peerCert.Elements(), peerCert.Length());
+ if (rv != Success) {
+ return rv;
+ }
+ BackCert cert(certDER, EndEntityOrCA::MustBeEndEntity, nullptr);
+ rv = cert.Init();
+ if (rv != Success) {
+ return rv;
+ }
+
+ Time notBefore(Time::uninitialized);
+ Time notAfter(Time::uninitialized);
+ rv = ParseValidity(cert.GetValidity(), &notBefore, &notAfter);
+ if (rv != Success) {
+ return rv;
+ }
+ // now we check that validity is not greater than 14 days
+ Duration certDuration(notBefore, notAfter);
+ if (certDuration > Duration(60 * 60 * 24 * 14)) {
+ return mozilla::pkix::Result::ERROR_VALIDITY_TOO_LONG;
+ }
+ Time now = Now();
+ // and if the certificate is actually valid?
+ rv = CheckValidity(now, notBefore, notAfter);
+ if (rv != Success) {
+ return rv;
+ }
+
+ mozilla::psm::ServerCertHashesTrustDomain trustDomain;
+ rv = CheckSubjectPublicKeyInfo(
+ cert.GetSubjectPublicKeyInfo(), trustDomain,
+ EndEntityOrCA::MustBeEndEntity /* should be ignored*/);
+ if (rv != Success) {
+ return rv;
+ }
+
+ // ok now the final check, calculate the hash and compare it:
+ // https://w3c.github.io/webtransport/#compute-a-certificate-hash
+ nsTArray<uint8_t> certHash;
+ if (NS_FAILED(Digest::DigestBuf(SEC_OID_SHA256, peerCert, certHash)) ||
+ certHash.Length() != SHA256_LENGTH) {
+ return mozilla::pkix::Result::ERROR_INVALID_ALGORITHM;
+ }
+
+ // https://w3c.github.io/webtransport/#verify-a-certificate-hash
+ for (const auto& hash : aServerCertHashes) {
+ nsCString algorithm;
+ if (NS_FAILED(hash->GetAlgorithm(algorithm)) || algorithm != "sha-256") {
+ continue;
+ }
+
+ nsTArray<uint8_t> value;
+ if (NS_FAILED(hash->GetValue(value))) {
+ continue;
+ }
+
+ if (certHash == value) {
+ return Success;
+ }
+ }
+ return mozilla::pkix::Result::ERROR_UNTRUSTED_CERT;
+}
+} // namespace mozilla::net
diff --git a/netwerk/protocol/http/WebTransportCertificateVerifier.h b/netwerk/protocol/http/WebTransportCertificateVerifier.h
new file mode 100644
index 0000000000..2045a95a89
--- /dev/null
+++ b/netwerk/protocol/http/WebTransportCertificateVerifier.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef mozilla_net_WebTransportCertificateVerifier_h
+#define mozilla_net_WebTransportCertificateVerifier_h
+
+#include "nsTArray.h"
+#include "nsIWebTransport.h"
+#include "nss/mozpkix/pkixtypes.h"
+
+namespace mozilla::net {
+// This is a special version for serverCertificateHashes introduced with
+// WebTransport
+mozilla::pkix::Result AuthCertificateWithServerCertificateHashes(
+ nsTArray<uint8_t>& peerCert,
+ const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes);
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_WebTransportCertificateVerifier_h
diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build
index db6b2a1a68..b090a56d88 100644
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -164,6 +164,7 @@ UNIFIED_SOURCES += [
"TlsHandshaker.cpp",
"TLSTransportLayer.cpp",
"TRRServiceChannel.cpp",
+ "WebTransportCertificateVerifier.cpp",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
diff --git a/netwerk/protocol/http/nsHttp.cpp b/netwerk/protocol/http/nsHttp.cpp
index 768ad91729..5425c3c7b9 100644
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -1017,13 +1017,15 @@ SupportedAlpnRank IsAlpnSupported(const nsACString& aAlpn) {
return SupportedAlpnRank::NOT_SUPPORTED;
}
-// On some security error when 0RTT is used we want to restart transactions
-// without 0RTT. Some firewalls do not behave well with 0RTT and cause this
-// errors.
-bool SecurityErrorThatMayNeedRestart(nsresult aReason) {
+// NSS Errors which *may* have been triggered by the use of 0-RTT in the
+// presence of badly behaving middleboxes. We may re-attempt the connection
+// without early data.
+bool PossibleZeroRTTRetryError(nsresult aReason) {
return (aReason ==
psm::GetXPCOMFromNSSError(SSL_ERROR_PROTOCOL_VERSION_ALERT)) ||
- (aReason == psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_MAC_ALERT));
+ (aReason == psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_MAC_ALERT)) ||
+ (aReason ==
+ psm::GetXPCOMFromNSSError(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT));
}
nsresult MakeOriginURL(const nsACString& origin, nsCOMPtr<nsIURI>& url) {
diff --git a/netwerk/protocol/http/nsHttp.h b/netwerk/protocol/http/nsHttp.h
index d0bd4cfe67..fc20351c9d 100644
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -499,8 +499,6 @@ static inline bool AllowedErrorForHTTPSRRFallback(nsresult aError) {
aError == NS_ERROR_UNKNOWN_HOST || aError == NS_ERROR_NET_TIMEOUT;
}
-bool SecurityErrorThatMayNeedRestart(nsresult aReason);
-
[[nodiscard]] nsresult MakeOriginURL(const nsACString& origin,
nsCOMPtr<nsIURI>& url);
@@ -521,6 +519,8 @@ uint64_t WebTransportErrorToHttp3Error(uint8_t aErrorCode);
uint8_t Http3ErrorToWebTransportError(uint64_t aErrorCode);
+bool PossibleZeroRTTRetryError(nsresult aReason);
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index b3806f761b..96e61e4fb8 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -9,11 +9,10 @@
#include <inttypes.h>
-#include "DocumentChannelParent.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
#include "mozilla/dom/nsCSPContext.h"
-#include "mozilla/dom/nsCSPService.h"
+#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "nsContentSecurityUtils.h"
@@ -29,7 +28,6 @@
#include "nsIEffectiveTLDService.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsINetworkInterceptController.h"
-#include "nsINSSErrorsService.h"
#include "nsIStringBundle.h"
#include "nsIStreamListenerTee.h"
#include "nsISeekableStream.h"
@@ -40,7 +38,6 @@
#include "nsMimeTypes.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
-#include "nsIURL.h"
#include "nsIStreamTransportService.h"
#include "prnetdb.h"
#include "nsEscape.h"
@@ -63,11 +60,8 @@
#include "mozilla/BasePrincipal.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/PerfStats.h"
-#include "mozilla/Preferences.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/Components.h"
-#include "mozilla/StaticPrefs_browser.h"
-#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StaticPrefs_security.h"
@@ -93,15 +87,12 @@
#include "nsIStreamConverterService.h"
#include "nsISiteSecurityService.h"
#include "nsString.h"
-#include "CacheObserver.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "mozilla/dom/ReferrerInfo.h"
#include "mozilla/Telemetry.h"
#include "AlternateServices.h"
#include "NetworkMarker.h"
#include "nsIHttpPushListener.h"
-#include "nsIX509Cert.h"
-#include "ScopedNSSTypes.h"
#include "nsIDNSRecord.h"
#include "mozilla/dom/Document.h"
#include "nsICompressConvStats.h"
@@ -116,14 +107,11 @@
#include "CacheStorageService.h"
#include "HttpChannelParent.h"
#include "HttpTransactionParent.h"
-#include "ParentChannelListener.h"
#include "ThirdPartyUtil.h"
#include "InterceptedHttpChannel.h"
#include "../../cache2/CacheFileUtils.h"
-#include "nsIMultiplexInputStream.h"
#include "nsINetworkLinkService.h"
#include "mozilla/ContentBlockingAllowList.h"
-#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/dom/nsHTTPSOnlyStreamListener.h"
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
@@ -134,7 +122,6 @@
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "HttpTrafficAnalyzer.h"
#include "mozilla/net/SocketProcessParent.h"
-#include "js/Conversions.h"
#include "mozilla/dom/SecFetch.h"
#include "mozilla/net/TRRService.h"
#include "nsUnknownDecoder.h"
@@ -560,7 +547,8 @@ nsresult nsHttpChannel::OnBeforeConnect() {
}
rv = sss->IsSecureURI(mURI, originAttributes, &isSecureURI);
NS_ENSURE_SUCCESS(rv, rv);
- // Save that on the loadInfo so it can later be consumed by SecurityInfo.jsm
+ // Save that on the loadInfo so it can later be consumed by
+ // SecurityInfo.sys.mjs
mLoadInfo->SetHstsStatus(isSecureURI);
RefPtr<mozilla::dom::BrowsingContext> bc;
@@ -797,6 +785,19 @@ nsresult nsHttpChannel::ContinueOnBeforeConnect(bool aShouldUpgrade,
mConnectionInfo->SetAnonymousAllowClientCert(
(mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) != 0);
+ if (mWebTransportSessionEventListener) {
+ nsTArray<RefPtr<nsIWebTransportHash>> aServerCertHashes;
+ nsresult rv;
+ nsCOMPtr<WebTransportConnectionSettings> wtconSettings =
+ do_QueryInterface(mWebTransportSessionEventListener, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ wtconSettings->GetServerCertificateHashes(aServerCertHashes);
+ gHttpHandler->ConnMgr()->StoreServerCertHashes(
+ mConnectionInfo, gHttpHandler->IsHttp2Excluded(mConnectionInfo),
+ !Http3Allowed(), std::move(aServerCertHashes));
+ }
+
// notify "http-on-before-connect" observers
gHttpHandler->OnBeforeConnect(this);
@@ -2277,8 +2278,10 @@ nsresult nsHttpChannel::ContinueProcessResponse1() {
// Given a successful connection, process any STS or PKP data that's
// relevant.
- DebugOnly<nsresult> rv = ProcessSecurityHeaders();
- MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
+ nsresult rv = ProcessSecurityHeaders();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("ProcessSTSHeader failed, continuing load.");
+ }
if ((httpStatus < 500) && (httpStatus != 421)) {
ProcessAltService();
@@ -2400,7 +2403,6 @@ nsresult nsHttpChannel::ContinueProcessResponse3(nsresult rv) {
rv = ProcessNormal();
}
break;
- case 300:
case 301:
case 302:
case 307:
@@ -6443,6 +6445,16 @@ nsresult nsHttpChannel::BeginConnect() {
connInfo =
new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo,
originAttributes, isHttps, true, true);
+ bool dedicated = true;
+ nsresult rv;
+ nsCOMPtr<WebTransportConnectionSettings> wtconSettings =
+ do_QueryInterface(mWebTransportSessionEventListener, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ wtconSettings->GetDedicated(&dedicated);
+ if (dedicated) {
+ connInfo->SetWebTransportId(
+ gHttpHandler->ConnMgr()->GenerateNewWebTransportId());
+ }
} else {
connInfo = new nsHttpConnectionInfo(host, port, ""_ns, mUsername,
proxyInfo, originAttributes, isHttps);
@@ -6954,7 +6966,54 @@ nsHttpChannel::OnProxyAvailable(nsICancelable* request, nsIChannel* channel,
// request was canceled. We just failover to DIRECT when proxy resolution
// fails (failure can mean that the PAC URL could not be loaded).
- if (NS_SUCCEEDED(status)) mProxyInfo = pi;
+ if (NS_SUCCEEDED(status)) {
+ mProxyInfo = pi;
+
+ if (mProxyInfo) {
+ nsAutoCStringN<8> type;
+ mProxyInfo->GetType(type);
+ uint32_t flags = 0;
+ mProxyInfo->GetFlags(&flags);
+
+ if (type.EqualsLiteral("socks")) {
+ if (flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks5h)
+ .Add(1);
+ } else {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks5)
+ .Add(1);
+ }
+ } else if (type.EqualsLiteral("socks4")) {
+ if (flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks4a)
+ .Add(1);
+ } else {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks4)
+ .Add(1);
+ }
+ } else if (type.EqualsLiteral("http")) {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eHttp)
+ .Add(1);
+ } else if (type.EqualsLiteral("https")) {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eHttps)
+ .Add(1);
+ } else if (type.EqualsLiteral("direct")) {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eDirect)
+ .Add(1);
+ } else {
+ glean::networking::proxy_info_type
+ .EnumGet(glean::networking::ProxyInfoTypeLabel::eUnknown)
+ .Add(1);
+ }
+ }
+ }
if (!gHttpHandler->Active()) {
LOG(
@@ -8203,6 +8262,10 @@ nsresult nsHttpChannel::ContinueOnStopRequest(nsresult aStatus, bool aIsFromNet,
}
mAuthRetryPending = false;
}
+
+ // notify "http-on-before-stop-request" observers
+ gHttpHandler->OnBeforeStopRequest(this);
+
if (mListener) {
LOG(("nsHttpChannel %p calling OnStopRequest\n", this));
MOZ_ASSERT(LoadOnStartRequestCalled(),
@@ -8219,7 +8282,7 @@ nsresult nsHttpChannel::ContinueOnStopRequest(nsresult aStatus, bool aIsFromNet,
mRedirectChannel = nullptr;
- // notify "http-on-stop-connect" observers
+ // notify "http-on-stop-request" observers
gHttpHandler->OnStopRequest(this);
RemoveAsNonTailRequest();
@@ -9342,8 +9405,8 @@ nsresult nsHttpChannel::OnPush(uint32_t aPushedStreamId, const nsACString& aUrl,
// static
bool nsHttpChannel::IsRedirectStatus(uint32_t status) {
// 305 disabled as a security measure (see bug 187996).
- return status == 300 || status == 301 || status == 302 || status == 303 ||
- status == 307 || status == 308;
+ return status == 301 || status == 302 || status == 303 || status == 307 ||
+ status == 308;
}
void nsHttpChannel::SetCouldBeSynthesized() {
diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp
index 023c06f3f3..cd41cd65bc 100644
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -742,7 +742,7 @@ void nsHttpConnection::Close(nsresult reason, bool aIsShutdown) {
gHttpHandler->ClearHostMapping(mConnInfo);
}
if (mTlsHandshaker->EarlyDataWasAvailable() &&
- SecurityErrorThatMayNeedRestart(reason)) {
+ PossibleZeroRTTRetryError(reason)) {
gHttpHandler->Exclude0RttTcp(mConnInfo);
}
diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.cpp b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
index e91128d85a..21c3e75b54 100644
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -258,6 +258,12 @@ void nsHttpConnectionInfo::BuildHashKey() {
}
}
+ if (mWebTransportId) {
+ mHashKey.AppendLiteral("{wId");
+ mHashKey.AppendInt(mWebTransportId, 16);
+ mHashKey.AppendLiteral("}");
+ }
+
nsAutoCString originAttributes;
mOriginAttributes.CreateSuffix(originAttributes);
mHashKey.Append(originAttributes);
@@ -326,6 +332,7 @@ already_AddRefed<nsHttpConnectionInfo> nsHttpConnectionInfo::Clone() const {
clone->SetIPv6Disabled(GetIPv6Disabled());
clone->SetHasIPHintAddress(HasIPHintAddress());
clone->SetEchConfig(GetEchConfig());
+ clone->SetWebTransportId(GetWebTransportId());
MOZ_ASSERT(clone->Equals(this));
return clone.forget();
@@ -418,6 +425,7 @@ void nsHttpConnectionInfo::SerializeHttpConnectionInfo(
aArgs.hasIPHintAddress() = aInfo->HasIPHintAddress();
aArgs.echConfig() = aInfo->GetEchConfig();
aArgs.webTransport() = aInfo->GetWebTransport();
+ aArgs.webTransportId() = aInfo->GetWebTransportId();
if (!aInfo->ProxyInfo()) {
return;
@@ -448,6 +456,8 @@ nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(
aInfoArgs.routedHost(), aInfoArgs.routedPort(), aInfoArgs.isHttp3(),
aInfoArgs.webTransport());
}
+ // Transfer Webtransport ids
+ cinfo->SetWebTransportId(aInfoArgs.webTransportId());
// Make sure the anonymous, insecure-scheme, and private flags are transferred
cinfo->SetAnonymous(aInfoArgs.anonymous());
@@ -542,6 +552,13 @@ void nsHttpConnectionInfo::SetWebTransport(bool aWebTransport) {
}
}
+void nsHttpConnectionInfo::SetWebTransportId(uint64_t id) {
+ if (mWebTransportId != id) {
+ mWebTransportId = id;
+ RebuildHashKey();
+ }
+}
+
void nsHttpConnectionInfo::SetTlsFlags(uint32_t aTlsFlags) {
mTlsFlags = aTlsFlags;
const uint32_t tlsFlagsLength = 8;
diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.h b/netwerk/protocol/http/nsHttpConnectionInfo.h
index 892d795b10..3757b87be7 100644
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -222,6 +222,9 @@ class nsHttpConnectionInfo final : public ARefBase {
void SetWebTransport(bool aWebTransport);
bool GetWebTransport() const { return mWebTransport; }
+ void SetWebTransportId(uint64_t id);
+ uint32_t GetWebTransportId() const { return mWebTransportId; };
+
const nsCString& GetNPNToken() { return mNPNToken; }
const nsCString& GetUsername() { return mUsername; }
@@ -307,6 +310,9 @@ class nsHttpConnectionInfo final : public ARefBase {
bool mHasIPHintAddress = false;
nsCString mEchConfig;
+ uint64_t mWebTransportId = 0; // current dedicated Id only used for
+ // Webtransport, zero means not dedicated
+
// for RefPtr
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpConnectionInfo, override)
};
diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
index 2e937d0f2a..c7c385a42a 100644
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -3821,6 +3821,62 @@ void nsHttpConnectionMgr::DecrementNumIdleConns() {
ConditionallyStopPruneDeadConnectionsTimer();
}
+// A structure used to marshall objects necessary for ServerCertificateHashaes
+class nsStoreServerCertHashesData : public ARefBase {
+ public:
+ nsStoreServerCertHashesData(
+ nsHttpConnectionInfo* aConnInfo, bool aNoSpdy, bool aNoHttp3,
+ nsTArray<RefPtr<nsIWebTransportHash>>&& aServerCertHashes)
+ : mConnInfo(aConnInfo),
+ mNoSpdy(aNoSpdy),
+ mNoHttp3(aNoHttp3),
+ mServerCertHashes(std::move(aServerCertHashes)) {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStoreServerCertHashesData, override)
+
+ RefPtr<nsHttpConnectionInfo> mConnInfo;
+ bool mNoSpdy;
+ bool mNoHttp3;
+ nsTArray<RefPtr<nsIWebTransportHash>> mServerCertHashes;
+
+ private:
+ virtual ~nsStoreServerCertHashesData() = default;
+};
+
+// The connection manager needs to know the hashes used for a WebTransport
+// connection authenticated with serverCertHashes
+nsresult nsHttpConnectionMgr::StoreServerCertHashes(
+ nsHttpConnectionInfo* aConnInfo, bool aNoSpdy, bool aNoHttp3,
+ nsTArray<RefPtr<nsIWebTransportHash>>&& aServerCertHashes) {
+ RefPtr<nsHttpConnectionInfo> ci = aConnInfo->Clone();
+ RefPtr<nsStoreServerCertHashesData> data = new nsStoreServerCertHashesData(
+ ci, aNoSpdy, aNoHttp3, std::move(aServerCertHashes));
+ return PostEvent(&nsHttpConnectionMgr::OnMsgStoreServerCertHashes, 0, data);
+}
+
+void nsHttpConnectionMgr::OnMsgStoreServerCertHashes(int32_t, ARefBase* param) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ nsStoreServerCertHashesData* data =
+ static_cast<nsStoreServerCertHashesData*>(param);
+
+ bool isWildcard;
+ ConnectionEntry* connEnt = GetOrCreateConnectionEntry(
+ data->mConnInfo, true, data->mNoSpdy, data->mNoHttp3, &isWildcard);
+ MOZ_ASSERT(!isWildcard, "No webtransport with wildcard");
+ connEnt->SetServerCertHashes(std::move(data->mServerCertHashes));
+}
+
+const nsTArray<RefPtr<nsIWebTransportHash>>*
+nsHttpConnectionMgr::GetServerCertHashes(nsHttpConnectionInfo* aConnInfo) {
+ ConnectionEntry* connEnt = mCT.GetWeak(aConnInfo->HashKey());
+ if (!connEnt) {
+ MOZ_ASSERT(0);
+ return nullptr;
+ }
+ return &connEnt->GetServerCertHashes();
+}
+
void nsHttpConnectionMgr::CheckTransInPendingQueue(nsHttpTransaction* aTrans) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
// We only do this check on socket thread. When this function is called on
diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h
index 2cf4ab7568..3be6b01689 100644
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -60,6 +60,12 @@ class nsHttpConnectionMgr final : public HttpConnectionMgrShell,
[[nodiscard]] nsresult CancelTransactions(nsHttpConnectionInfo*,
nsresult code);
+ // The connection manager needs to know the hashes used for a WebTransport
+ // connection authenticated with serverCertHashes
+ nsresult StoreServerCertHashes(
+ nsHttpConnectionInfo* aConnInfo, bool aNoSpdy, bool aNoHttp3,
+ nsTArray<RefPtr<nsIWebTransportHash>>&& aServerCertHashes);
+
//-------------------------------------------------------------------------
// NOTE: functions below may be called only on the socket thread.
//-------------------------------------------------------------------------
@@ -143,6 +149,11 @@ class nsHttpConnectionMgr final : public HttpConnectionMgrShell,
void NewIdleConnectionAdded(uint32_t timeToLive);
void DecrementNumIdleConns();
+ const nsTArray<RefPtr<nsIWebTransportHash>>* GetServerCertHashes(
+ nsHttpConnectionInfo* aConnInfo);
+
+ uint64_t GenerateNewWebTransportId() { return mMaxWebTransportId++; }
+
private:
virtual ~nsHttpConnectionMgr();
@@ -334,6 +345,7 @@ class nsHttpConnectionMgr final : public HttpConnectionMgrShell,
void OnMsgPruneNoTraffic(int32_t, ARefBase*);
void OnMsgUpdateCurrentBrowserId(int32_t, ARefBase*);
void OnMsgClearConnectionHistory(int32_t, ARefBase*);
+ void OnMsgStoreServerCertHashes(int32_t, ARefBase*);
// Total number of active connections in all of the ConnectionEntry objects
// that are accessed from mCT connection table.
@@ -462,6 +474,10 @@ class nsHttpConnectionMgr final : public HttpConnectionMgrShell,
void NotifyConnectionOfBrowserIdChange(uint64_t previousId);
void CheckTransInPendingQueue(nsHttpTransaction* aTrans);
+
+ // Used for generating unique IDSs for dedicated connections, currently used
+ // by WebTransport
+ Atomic<uint64_t> mMaxWebTransportId{1};
};
} // namespace mozilla::net
diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
index dcc5307984..4861b45466 100644
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -1397,13 +1397,6 @@ void nsHttpHandler::PrefsChanged(const char* pref) {
}
}
- if (PREF_CHANGED(HTTP_PREF("max_response_header_size"))) {
- rv = Preferences::GetInt(HTTP_PREF("max_response_header_size"), &val);
- if (NS_SUCCEEDED(rv)) {
- mMaxHttpResponseHeaderSize = val;
- }
- }
-
if (PREF_CHANGED(HTTP_PREF("throttle.enable"))) {
rv = Preferences::GetBool(HTTP_PREF("throttle.enable"), &mThrottleEnabled);
if (NS_SUCCEEDED(rv) && mConnMgr) {
diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h
index cf745347ee..2562d0c833 100644
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -375,7 +375,12 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
NotifyObservers(chan, NS_DOCUMENT_ON_MODIFY_REQUEST_TOPIC);
}
- // Called by the channel before writing a request
+ // Called by the channel before calling onStopRequest
+ void OnBeforeStopRequest(nsIHttpChannel* chan) {
+ NotifyObservers(chan, NS_HTTP_ON_BEFORE_STOP_REQUEST_TOPIC);
+ }
+
+ // Called by the channel after calling onStopRequest
void OnStopRequest(nsIHttpChannel* chan) {
NotifyObservers(chan, NS_HTTP_ON_STOP_REQUEST_TOPIC);
}
@@ -447,10 +452,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
return (uint16_t)mHttp3MaxBlockedStreams;
}
- uint32_t MaxHttpResponseHeaderSize() const {
- return mMaxHttpResponseHeaderSize;
- }
-
const nsCString& Http3QlogDir();
float FocusedWindowTransactionRatio() const {
@@ -730,9 +731,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
nsCString mHttp3QlogDir;
- // The max size (in bytes) for received Http response header.
- uint32_t mMaxHttpResponseHeaderSize{393216};
-
// The ratio for dispatching transactions from the focused window.
float mFocusedWindowTransactionRatio{0.9f};
diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp
index 580ea35841..558de8e6ea 100644
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -217,6 +217,13 @@ nsresult nsHttpTransaction::Init(
LOG1(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ LOG(
+ ("nsHttpTransaction aborting init because of app"
+ "shutdown"));
+ return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+ }
+
MOZ_ASSERT(cinfo);
MOZ_ASSERT(requestHead);
MOZ_ASSERT(target);
@@ -1334,7 +1341,7 @@ bool nsHttpTransaction::ShouldRestartOn0RttError(nsresult reason) {
"mEarlyDataWasAvailable=%d error=%" PRIx32 "]\n",
this, mEarlyDataWasAvailable, static_cast<uint32_t>(reason)));
return StaticPrefs::network_http_early_data_disable_on_error() &&
- mEarlyDataWasAvailable && SecurityErrorThatMayNeedRestart(reason);
+ mEarlyDataWasAvailable && PossibleZeroRTTRetryError(reason);
}
static void MaybeRemoveSSLToken(nsITransportSecurityInfo* aSecurityInfo) {
@@ -1501,7 +1508,7 @@ void nsHttpTransaction::Close(nsresult reason) {
if (reason ==
psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA) ||
- reason == psm::GetXPCOMFromNSSError(SSL_ERROR_PROTOCOL_VERSION_ALERT) ||
+ PossibleZeroRTTRetryError(reason) ||
(!mReceivedData && ((mRequestHead && mRequestHead->IsSafeMethod()) ||
!reallySentData || connReused)) ||
shouldRestartTransactionForHTTPSRR) {
@@ -1542,9 +1549,8 @@ void nsHttpTransaction::Close(nsresult reason) {
} else if (reason == psm::GetXPCOMFromNSSError(
SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA)) {
SetRestartReason(TRANSACTION_RESTART_DOWNGRADE_WITH_EARLY_DATA);
- } else if (reason ==
- psm::GetXPCOMFromNSSError(SSL_ERROR_PROTOCOL_VERSION_ALERT)) {
- SetRestartReason(TRANSACTION_RESTART_PROTOCOL_VERSION_ALERT);
+ } else if (PossibleZeroRTTRetryError(reason)) {
+ SetRestartReason(TRANSACTION_RESTART_POSSIBLE_0RTT_ERROR);
}
// if restarting fails, then we must proceed to close the pipe,
// which will notify the channel that the transaction failed.
@@ -2520,7 +2526,7 @@ nsresult nsHttpTransaction::ProcessData(char* buf, uint32_t count,
mCurrentHttpResponseHeaderSize += bytesConsumed;
if (mCurrentHttpResponseHeaderSize >
- gHttpHandler->MaxHttpResponseHeaderSize()) {
+ StaticPrefs::network_http_max_response_header_size()) {
LOG(("nsHttpTransaction %p The response header exceeds the limit.\n",
this));
return NS_ERROR_FILE_TOO_BIG;
diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h
index 354fa2c4d2..5256de4cf2 100644
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -299,6 +299,7 @@ class nsHttpTransaction final : public nsAHttpTransaction,
TRANSACTION_RESTART_HTTP3_FAST_FALLBACK,
TRANSACTION_RESTART_OTHERS,
TRANSACTION_RESTART_PROTOCOL_VERSION_ALERT,
+ TRANSACTION_RESTART_POSSIBLE_0RTT_ERROR
};
void SetRestartReason(TRANSACTION_RESTART_REASON aReason);
diff --git a/netwerk/protocol/http/nsIHttpProtocolHandler.idl b/netwerk/protocol/http/nsIHttpProtocolHandler.idl
index d6a6f8a0c1..03d1d6dfe5 100644
--- a/netwerk/protocol/http/nsIHttpProtocolHandler.idl
+++ b/netwerk/protocol/http/nsIHttpProtocolHandler.idl
@@ -206,6 +206,12 @@ interface nsIHttpProtocolHandler : nsIProxiedProtocolHandler
#define NS_HTTP_ON_EXAMINE_CACHED_RESPONSE_TOPIC "http-on-examine-cached-response"
/**
+ * This topic is notified for every http channel before calling
+ * OnStopRequest on its listener.
+ */
+#define NS_HTTP_ON_BEFORE_STOP_REQUEST_TOPIC "http-on-before-stop-request"
+
+/**
* This topic is notified for every http channel right after it called
* OnStopRequest on its listener, regardless whether it was finished
* successfully, failed or has been canceled.
diff --git a/netwerk/protocol/res/PageThumbProtocolHandler.cpp b/netwerk/protocol/res/PageThumbProtocolHandler.cpp
index 5918b40fad..2d9777da7b 100644
--- a/netwerk/protocol/res/PageThumbProtocolHandler.cpp
+++ b/netwerk/protocol/res/PageThumbProtocolHandler.cpp
@@ -207,7 +207,7 @@ bool PageThumbProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
const nsACString& aPath,
const nsACString& aPathname,
nsACString& aResult) {
- // This should match the scheme in PageThumbs.jsm. We will only resolve
+ // This should match the scheme in PageThumbs.sys.mjs. We will only resolve
// URIs for thumbnails generated by PageThumbs here.
if (!aHost.EqualsLiteral(PAGE_THUMB_HOST) &&
!aHost.EqualsLiteral(PLACES_PREVIEWS_HOST)) {
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
index e650a74d5b..dd4ba7e7d0 100644
--- a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
+++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
@@ -27,7 +27,8 @@ namespace mozilla::net {
LazyLogModule webTransportLog("nsWebTransport");
NS_IMPL_ISUPPORTS(WebTransportSessionProxy, WebTransportSessionEventListener,
- nsIWebTransport, nsIRedirectResultListener, nsIStreamListener,
+ WebTransportConnectionSettings, nsIWebTransport,
+ nsIRedirectResultListener, nsIStreamListener,
nsIChannelEventSink, nsIInterfaceRequestor);
WebTransportSessionProxy::WebTransportSessionProxy()
@@ -63,17 +64,17 @@ WebTransportSessionProxy::~WebTransportSessionProxy() {
//-----------------------------------------------------------------------------
nsresult WebTransportSessionProxy::AsyncConnect(
- nsIURI* aURI,
+ nsIURI* aURI, bool aDedicated,
const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes,
nsIPrincipal* aPrincipal, uint32_t aSecurityFlags,
WebTransportSessionEventListener* aListener) {
- return AsyncConnectWithClient(aURI, std::move(aServerCertHashes), aPrincipal,
- aSecurityFlags, aListener,
+ return AsyncConnectWithClient(aURI, aDedicated, std::move(aServerCertHashes),
+ aPrincipal, aSecurityFlags, aListener,
Maybe<dom::ClientInfo>());
}
nsresult WebTransportSessionProxy::AsyncConnectWithClient(
- nsIURI* aURI,
+ nsIURI* aURI, bool aDedicated,
const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes,
nsIPrincipal* aPrincipal, uint32_t aSecurityFlags,
WebTransportSessionEventListener* aListener,
@@ -126,7 +127,10 @@ nsresult WebTransportSessionProxy::AsyncConnectWithClient(
return NS_ERROR_ABORT;
}
+ mDedicatedConnection = aDedicated;
+
if (!aServerCertHashes.IsEmpty()) {
+ mServerCertHashes.Clear();
mServerCertHashes.AppendElements(aServerCertHashes);
}
@@ -235,6 +239,18 @@ WebTransportSessionProxy::CloseSession(uint32_t status,
return NS_OK;
}
+NS_IMETHODIMP WebTransportSessionProxy::GetDedicated(bool* dedicated) {
+ *dedicated = mDedicatedConnection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebTransportSessionProxy::GetServerCertificateHashes(
+ nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes) {
+ aServerCertHashes.Clear();
+ aServerCertHashes.AppendElements(mServerCertHashes);
+ return NS_OK;
+}
+
void WebTransportSessionProxy::CloseSessionInternalLocked() {
MutexAutoLock lock(mMutex);
CloseSessionInternal();
@@ -573,8 +589,7 @@ WebTransportSessionProxy::OnStartRequest(nsIRequest* aRequest) {
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
if (!httpChannel ||
NS_FAILED(httpChannel->GetResponseStatus(&status)) ||
- !(status >= 200 && status < 300) ||
- !CheckServerCertificateIfNeeded()) {
+ !(status >= 200 && status < 300)) {
listener = mListener;
mListener = nullptr;
mChannel = nullptr;
@@ -990,57 +1005,6 @@ void WebTransportSessionProxy::CallOnSessionClosed() {
}
}
-bool WebTransportSessionProxy::CheckServerCertificateIfNeeded() {
- if (mServerCertHashes.IsEmpty()) {
- return true;
- }
-
- nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
- MOZ_ASSERT(httpChannel, "Not a http channel ?");
- nsCOMPtr<nsITransportSecurityInfo> tsi;
- httpChannel->GetSecurityInfo(getter_AddRefs(tsi));
- MOZ_ASSERT(tsi,
- "We shouln't reach this code before setting the security info.");
- nsCOMPtr<nsIX509Cert> cert;
- nsresult rv = tsi->GetServerCert(getter_AddRefs(cert));
- if (!cert || NS_WARN_IF(NS_FAILED(rv))) return true;
- nsTArray<uint8_t> certDER;
- if (NS_FAILED(cert->GetRawDER(certDER))) {
- return false;
- }
- // https://w3c.github.io/webtransport/#compute-a-certificate-hash
- nsTArray<uint8_t> certHash;
- if (NS_FAILED(Digest::DigestBuf(SEC_OID_SHA256, certDER.Elements(),
- certDER.Length(), certHash)) ||
- certHash.Length() != SHA256_LENGTH) {
- return false;
- }
- auto verifyCertDer = [&certHash](const auto& hash) {
- return certHash.Length() == hash.Length() &&
- memcmp(certHash.Elements(), hash.Elements(), certHash.Length()) == 0;
- };
-
- // https://w3c.github.io/webtransport/#verify-a-certificate-hash
- for (const auto& hash : mServerCertHashes) {
- nsCString algorithm;
- if (NS_FAILED(hash->GetAlgorithm(algorithm)) || algorithm != "sha-256") {
- continue;
- LOG(("Unexpected non-SHA-256 hash"));
- }
-
- nsTArray<uint8_t> value;
- if (NS_FAILED(hash->GetValue(value))) {
- continue;
- LOG(("Unexpected corrupted hash"));
- }
-
- if (verifyCertDer(value)) {
- return true;
- }
- }
- return false;
-}
-
void WebTransportSessionProxy::ChangeState(
WebTransportSessionProxyState newState) {
mMutex.AssertCurrentThreadOwns();
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.h b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
index aebb446e21..9e6f785d80 100644
--- a/netwerk/protocol/webtransport/WebTransportSessionProxy.h
+++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
@@ -121,6 +121,7 @@ class WebTransportStreamCallbackWrapper;
class WebTransportSessionProxy final : public nsIWebTransport,
public WebTransportSessionEventListener,
+ public WebTransportConnectionSettings,
public nsIStreamListener,
public nsIChannelEventSink,
public nsIRedirectResultListener,
@@ -129,6 +130,7 @@ class WebTransportSessionProxy final : public nsIWebTransport,
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBTRANSPORT
NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENER
+ NS_DECL_WEBTRANSPORTCONNECTIONSETTINGS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICHANNELEVENTSINK
@@ -170,11 +172,9 @@ class WebTransportSessionProxy final : public nsIWebTransport,
void OnMaxDatagramSizeInternal(uint64_t aSize);
void OnOutgoingDatagramOutComeInternal(
uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome);
- bool CheckServerCertificateIfNeeded();
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIChannel> mRedirectChannel;
- nsTArray<RefPtr<nsIWebTransportHash>> mServerCertHashes;
nsCOMPtr<WebTransportSessionEventListener> mListener MOZ_GUARDED_BY(mMutex);
RefPtr<Http3WebTransportSession> mWebTransportSession MOZ_GUARDED_BY(mMutex);
uint64_t mSessionId MOZ_GUARDED_BY(mMutex) = UINT64_MAX;
@@ -188,6 +188,8 @@ class WebTransportSessionProxy final : public nsIWebTransport,
nsTArray<std::function<void(nsresult)>> mPendingCreateStreamEvents
MOZ_GUARDED_BY(mMutex);
nsCOMPtr<nsIEventTarget> mTarget MOZ_GUARDED_BY(mMutex);
+ nsTArray<RefPtr<nsIWebTransportHash>> mServerCertHashes;
+ bool mDedicatedConnection; // for WebTranport
};
} // namespace mozilla::net
diff --git a/netwerk/protocol/webtransport/nsIWebTransport.idl b/netwerk/protocol/webtransport/nsIWebTransport.idl
index faf99e61b6..2283c42977 100644
--- a/netwerk/protocol/webtransport/nsIWebTransport.idl
+++ b/netwerk/protocol/webtransport/nsIWebTransport.idl
@@ -38,12 +38,14 @@ interface nsIWebTransport : nsISupports {
// When called, perform steps in "Initialization WebTransport over HTTP".
void asyncConnect(in nsIURI aURI,
+ in boolean aDedicated,
in Array<nsIWebTransportHash> aServerCertHashes,
in nsIPrincipal aLoadingPrincipal,
in unsigned long aSecurityFlags,
in WebTransportSessionEventListener aListener);
void asyncConnectWithClient(in nsIURI aURI,
+ in boolean aDedicated,
in Array<nsIWebTransportHash> aServerCertHashes,
in nsIPrincipal aLoadingPrincipal,
in unsigned long aSecurityFlags,
@@ -116,6 +118,13 @@ interface WebTransportSessionEventListener : nsISupports {
// void onStatsAvailable(in WebTransportStats aStats);
};
+[uuid(faad75bd-83c6-420b-9fdb-a70bd70be449)]
+interface WebTransportConnectionSettings : nsISupports {
+ // WebTransport specific connection information
+ readonly attribute bool dedicated;
+ void getServerCertificateHashes(out Array<nsIWebTransportHash> aServerCertHashes);
+};
+
// This interface is used as a callback when creating an outgoing
// unidirectional or bidirectional stream.
[scriptable, uuid(c6eeff1d-599b-40a8-9157-c7a40c3d51a2)]