commit fec1a197f44cb4e260ccd86069182491d70b7d42
parent 5cdbe885bc5e31877e42acc272f3312681168f31
Author: Randell Jesup <rjesup@mozilla.com>
Date: Tue, 7 Oct 2025 14:07:03 +0000
Bug 1917965: Add support for dcb and dcz content-encoding r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D258124
Diffstat:
14 files changed, 286 insertions(+), 110 deletions(-)
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
@@ -1168,7 +1168,8 @@ pref("network.http.redirection-limit", 20);
pref("network.http.accept-encoding", "gzip, deflate");
pref("network.http.accept-encoding.secure", "gzip, deflate, br, zstd");
// dictionary compression is always only for secure connections
-pref("network.http.accept-encoding.dictionary", "gzip, deflate, br, zstd, dcb, dcz");
+// Added to network.http.accept-encoding.secure
+pref("network.http.accept-encoding.dictionary", "dcb, dcz");
// Prompt for redirects resulting in unsafe HTTP requests
pref("network.http.prompt-temp-redirect", false);
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -376,6 +376,13 @@ nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps,
rv = mRequestHead.SetHeader(nsHttp::Host, hostLine);
if (NS_FAILED(rv)) return rv;
+ rv = gHttpHandler->AddAcceptAndDictionaryHeaders(aURI, &mRequestHead, isHTTPS,
+ mDict);
+ if (NS_FAILED(rv)) return rv;
+ if (mDict) {
+ mDict->InUse();
+ }
+
// Override the Accept header if a specific MediaDocument kind is forced.
ExtContentPolicy contentPolicyType =
mLoadInfo->GetExternalContentPolicyType();
@@ -395,7 +402,7 @@ nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps,
}
rv = gHttpHandler->AddStandardRequestHeaders(
- &mRequestHead, isHTTPS, aURI, contentPolicyType,
+ &mRequestHead, aURI, contentPolicyType,
nsContentUtils::ShouldResistFingerprinting(this,
RFPTarget::HttpUserAgent));
if (NS_FAILED(rv)) return rv;
@@ -1472,6 +1479,23 @@ HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
LOG(("HttpBaseChannel::DoApplyContentConversions [this=%p]\n", this));
+#ifdef DEBUG
+ {
+ nsAutoCString contentEncoding;
+ nsresult rv =
+ mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
+ if (NS_SUCCEEDED(rv) && !contentEncoding.IsEmpty()) {
+ nsAutoCString newEncoding;
+ char* cePtr = contentEncoding.BeginWriting();
+ while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) {
+ if (strcmp(val, "dcb") == 0 || strcmp(val, "dcz") == 0) {
+ MOZ_ASSERT(LoadApplyConversion() && !LoadHasAppliedConversion());
+ }
+ }
+ }
+ }
+#endif
+
if (!LoadApplyConversion()) {
LOG(("not applying conversion per ApplyConversion\n"));
return NS_OK;
@@ -1502,12 +1526,12 @@ HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
// being a stack with the last converter created being the first one
// to accept the raw network data.
+ nsAutoCString newEncoding;
char* cePtr = contentEncoding.BeginWriting();
uint32_t count = 0;
while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) {
if (++count > 16) {
- // That's ridiculous. We only understand 2 different ones :)
- // but for compatibility with old code, we will just carry on without
+ // For compatibility with old code, we will just carry on without
// removing the encodings
LOG(("Too many Content-Encodings. Ignoring remainder.\n"));
break;
@@ -1543,8 +1567,15 @@ HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
nextListener = converter;
} else {
if (val) LOG(("Unknown content encoding '%s', ignoring\n", val));
+ // leave it in content-encoding if we didn't decompress it, though if
+ // there are following decoders, this will just be wrong, and we
+ // should error out. Or maybe error out on any unknown encoding
+ newEncoding += val;
}
}
+
+ rv = mResponseHead->SetHeader(nsHttp::Content_Encoding, newEncoding);
+
*aNewNextListener = do_AddRef(nextListener).take();
return NS_OK;
}
diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h
@@ -84,6 +84,13 @@ class HttpChannelChild final : public PHttpChannelChild,
// nsIChannel
NS_IMETHOD GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) override;
NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
+ NS_IMETHOD GetDictionary(DictionaryCacheEntry** aDictionary) override {
+ *aDictionary = nullptr;
+ return NS_OK;
+ }
+ NS_IMETHOD SetDictionary(DictionaryCacheEntry* aDictionary) override {
+ return NS_OK;
+ }
// HttpBaseChannel::nsIHttpChannel
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
diff --git a/netwerk/protocol/http/InterceptedHttpChannel.h b/netwerk/protocol/http/InterceptedHttpChannel.h
@@ -319,6 +319,14 @@ class InterceptedHttpChannel final
void DoNotifyListenerCleanup() override;
void DoAsyncAbort(nsresult aStatus) override;
+
+ NS_IMETHOD GetDictionary(DictionaryCacheEntry** aDictionary) override {
+ *aDictionary = nullptr;
+ return NS_OK;
+ }
+ NS_IMETHOD SetDictionary(DictionaryCacheEntry* aDictionary) override {
+ return NS_OK;
+ }
};
} // namespace mozilla::net
diff --git a/netwerk/protocol/http/NullHttpChannel.cpp b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -846,6 +846,19 @@ NullHttpChannel::GetRenderBlocking(bool* aRenderBlocking) {
return NS_ERROR_NOT_IMPLEMENTED;
}
+NS_IMETHODIMP
+NullHttpChannel::GetDictionary(
+ mozilla::net::DictionaryCacheEntry** aDictionary) {
+ *aDictionary = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetDictionary(
+ mozilla::net::DictionaryCacheEntry* aDictionary) {
+ return NS_OK;
+}
+
#define IMPL_TIMING_ATTR(name) \
NS_IMETHODIMP \
NullHttpChannel::Get##name##Time(PRTime* _retval) { \
diff --git a/netwerk/protocol/http/ObliviousHttpChannel.cpp b/netwerk/protocol/http/ObliviousHttpChannel.cpp
@@ -880,4 +880,15 @@ NS_IMETHODIMP ObliviousHttpChannel::GetDocumentCharacterSet(
return NS_ERROR_NOT_IMPLEMENTED;
}
+NS_IMETHODIMP ObliviousHttpChannel::GetDictionary(
+ DictionaryCacheEntry** aDictionary) {
+ *aDictionary = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ObliviousHttpChannel::SetDictionary(
+ DictionaryCacheEntry* aDictionary) {
+ return NS_OK;
+}
+
} // namespace mozilla::net
diff --git a/netwerk/protocol/http/TRRServiceChannel.h b/netwerk/protocol/http/TRRServiceChannel.h
@@ -76,6 +76,13 @@ class TRRServiceChannel : public HttpBaseChannel,
NS_IMETHOD SetNotificationCallbacks(
nsIInterfaceRequestor* aCallbacks) override;
+ NS_IMETHOD GetDictionary(DictionaryCacheEntry** aDictionary) override {
+ *aDictionary = nullptr;
+ return NS_OK;
+ }
+ NS_IMETHOD SetDictionary(DictionaryCacheEntry* aDictionary) override {
+ return NS_OK;
+ }
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
// nsIClassOfService
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -150,6 +150,9 @@
# include "mozilla/StaticPrefs_fuzzing.h"
#endif
+// TODO replace in later patch
+#define LOG_DICTIONARIES(x) LOG(x)
+
namespace mozilla {
using namespace dom;
@@ -504,6 +507,10 @@ nsHttpChannel::~nsHttpChannel() {
if (gHttpHandler) {
gHttpHandler->RemoveHttpChannel(mChannelId);
}
+
+ if (mDict) {
+ mDict->UseCompleted();
+ }
}
void nsHttpChannel::ReleaseMainThreadOnlyReferences() {
@@ -1960,8 +1967,8 @@ nsresult nsHttpChannel::SetupChannelForTransaction() {
// override that, so we don't try. Section 4.5 step 8.19 and 8.20 of
// HTTP-network-or-cache fetch
// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
- rv = mHttpHandler->AddEncodingHeaders(&mRequestHead,
- mURI->SchemeIs("https"), mURI);
+ rv = mHttpHandler->AddAcceptAndDictionaryHeaders(
+ mURI, &mRequestHead, mURI->SchemeIs("https"), mDict);
if (NS_FAILED(rv)) return rv;
}
@@ -3502,8 +3509,34 @@ nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) {
if (mCacheEntry) {
rv = InitCacheEntry();
if (NS_FAILED(rv)) CloseCacheEntry(true);
+ } else {
+ // We may still need to decompress in the parent if it's dcb or dcz
+ nsAutoCString contentEncoding;
+ Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding,
+ contentEncoding);
+ if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) {
+ LOG_DICTIONARIES(
+ ("Removing Content-Encoding %s for %p", contentEncoding.get(), this));
+ nsCOMPtr<nsIStreamListener> listener;
+ // otherwise we won't convert in the parent process
+ // XXX may be redundant, but safe
+ SetApplyConversion(true);
+ rv = DoApplyContentConversions(mListener, getter_AddRefs(listener),
+ nullptr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (listener) {
+ LOG_DICTIONARIES(("Installed nsHTTPCompressConv %p without cache tee",
+ listener.get()));
+ mListener = listener;
+ mCompressListener = listener;
+ StoreHasAppliedConversion(true);
+ } else {
+ LOG_DICTIONARIES(("Didn't install decompressor without cache tee"));
+ }
+ }
}
-
// Check that the server sent us what we were asking for
if (LoadResuming()) {
// Create an entity id from the response
@@ -3530,15 +3563,19 @@ nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) {
}
}
- rv = CallOnStartRequest();
- if (NS_FAILED(rv)) return rv;
+ // We need to do this before CallonStartRequest, since this can modify
+ // the Content-Encoding to remove dcb/dcz (and perhaps others), and
+ // CallOnStartRequest() sends this to the content process.
- // install cache listener if we still have a cache entry open
+ // Install cache listener if we still have a cache entry open
if (mCacheEntry && !LoadCacheEntryIsReadOnly()) {
rv = InstallCacheListener();
if (NS_FAILED(rv)) return rv;
}
+ rv = CallOnStartRequest();
+ if (NS_FAILED(rv)) return rv;
+
return NS_OK;
}
@@ -6008,28 +6045,14 @@ nsresult nsHttpChannel::InstallCacheListener(int64_t offset) {
mRaceCacheWithNetwork);
MOZ_ASSERT(mListener);
- nsAutoCString contentEncoding, contentType;
- Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
- mResponseHead->ContentType(contentType);
- // If the content is compressible and the server has not compressed it,
- // mark the cache entry for compression.
- if (contentEncoding.IsEmpty() &&
- (contentType.EqualsLiteral(TEXT_HTML) ||
- contentType.EqualsLiteral(TEXT_PLAIN) ||
- contentType.EqualsLiteral(TEXT_CSS) ||
- contentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
- contentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
- contentType.EqualsLiteral(TEXT_XML) ||
- contentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
- contentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
- contentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
- contentType.EqualsLiteral(APPLICATION_XHTML_XML))) {
- rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0");
- if (NS_FAILED(rv)) {
- LOG(("unable to mark cache entry for compression"));
- }
+ // XXX We may want to consider recompressing any dcb/dcz files to save space
+ // and improve hitrate. Downside is CPU use, complexity and perhaps delay,
+ // maybe.
+ nsAutoCString dictionary;
+ Unused << mResponseHead->GetHeader(nsHttp::Use_As_Dictionary, dictionary);
+ if (!dictionary.IsEmpty()) {
+ mCacheEntry->SetDictionary(mDict);
}
-
LOG(("Trading cache input stream for output stream [channel=%p]", this));
// We must close the input stream first because cache entries do not
@@ -6081,12 +6104,44 @@ nsresult nsHttpChannel::InstallCacheListener(int64_t offset) {
do_CreateInstance(kStreamListenerTeeCID, &rv);
if (NS_FAILED(rv)) return rv;
+ rv = tee->Init(mListener, out, nullptr);
LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%" PRIx32, tee.get(),
static_cast<uint32_t>(rv)));
- rv = tee->Init(mListener, out, nullptr);
if (NS_FAILED(rv)) return rv;
-
mListener = tee;
+
+ // If this is Use-As-Dictionary we need to be able to read it quickly for
+ // dictionary use, OR if it's encoded in dcb or dcz (using a dictionary),
+ // we must decompress it before storing since we won't have the dictionary
+ // when we go to read it out later.
+ // In this case, we hook an nsHTTPCompressConv instance in before the tee
+ // since we don't want to have to decompress it here and again in the content
+ // process (if it's not dcb/dcz); if it is dcb/dcz we must decompress it
+ // before the content process gets to see it
+ nsAutoCString contentEncoding;
+ Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
+ LOG(("Content-Encoding for %p: %s", this,
+ PromiseFlatCString(contentEncoding).get()));
+ if (!dictionary.IsEmpty() || contentEncoding.Equals("dcb") ||
+ contentEncoding.Equals("dcz")) {
+ nsCOMPtr<nsIStreamListener> listener;
+ // otherwise we won't convert in the parent process
+ SetApplyConversion(true);
+ rv =
+ DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (listener) {
+ LOG(("Installed nsHTTPCompressConv %p before tee", listener.get()));
+ mListener = listener;
+ mCompressListener = listener;
+ StoreHasAppliedConversion(true);
+ } else
+ LOG(("Didn't install decompressor before tee"));
+ }
+
+ // XXX telemetry as to how often we get dcb/dcz
return NS_OK;
}
@@ -7700,6 +7755,24 @@ nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
return NS_OK;
}
+NS_IMETHODIMP
+nsHttpChannel::GetDictionary(DictionaryCacheEntry** aDictionary) {
+ *aDictionary = do_AddRef(mDict).take();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::SetDictionary(DictionaryCacheEntry* aDictionary) {
+ if (mDict) {
+ mDict->UseCompleted();
+ }
+ mDict = aDictionary;
+ if (aDictionary) {
+ aDictionary->InUse();
+ }
+ return NS_OK;
+}
+
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIHttpChannelInternal
//-----------------------------------------------------------------------------
diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
@@ -205,6 +205,9 @@ class nsHttpChannel final : public HttpBaseChannel,
NS_IMETHOD SetResponseStatus(uint32_t aStatus,
const nsACString& aStatusText) override;
+ NS_IMETHOD GetDictionary(DictionaryCacheEntry** aDictionary) override;
+ NS_IMETHOD SetDictionary(DictionaryCacheEntry* aDictionary) override;
+
void SetWarningReporter(HttpChannelSecurityWarningReporter* aReporter);
HttpChannelSecurityWarningReporter* GetWarningReporter();
diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -27,6 +27,7 @@
#include "nsCOMPtr.h"
#include "nsNetCID.h"
#include "mozilla/AppShutdown.h"
+#include "mozilla/Base64.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
#include "mozilla/Printf.h"
@@ -653,58 +654,72 @@ nsresult nsHttpHandler::InitConnectionMgr() {
// We're using RequestOverride because this can get called when these are
// set by Fetch from the old request
-// only call if we're https!
nsresult nsHttpHandler::AddAcceptAndDictionaryHeaders(
- nsIURI* aURI, nsHttpRequestHead* aRequest) {
+ nsIURI* aURI, nsHttpRequestHead* aRequest, bool aSecure,
+ RefPtr<DictionaryCacheEntry>& aDict) {
LOG(("Adding Dictionary headers"));
nsresult rv;
- // The dictionary info may require us to check the cache.
- // XXX This would require that AddAcceptAndDictionaryHeaders be effectively
- // async, perhaps by passing a lambda in.
- RefPtr<DictionaryCacheEntry> dict =
- mDictionaryCache ? mDictionaryCache->GetDictionaryFor(aURI) : nullptr;
- if (dict) {
- rv =
- aRequest->SetHeader(nsHttp::Accept_Encoding, mDictionaryAcceptEncodings,
- false, nsHttpHeaderArray::eVarietyRequestOverride);
- if (NS_FAILED(rv)) {
- return rv;
- }
- LOG(("Setting Accept-Encoding: %s",
- PromiseFlatCString(mDictionaryAcceptEncodings).get()));
+ // Add the "Accept-Encoding" header and possibly Dictionary headers
+ if (aSecure) {
+ // The dictionary info may require us to check the cache.
+ // XXX This would require that AddAcceptAndDictionaryHeaders be effectively
+ // async, perhaps by passing a lambda to call AddAcceptAndDictionaryHeaders
+ // and then unblock the request
+ aDict =
+ mDictionaryCache ? mDictionaryCache->GetDictionaryFor(aURI) : nullptr;
+ if (aDict) {
+ rv = aRequest->SetHeader(nsHttp::Accept_Encoding,
+ mDictionaryAcceptEncodings, false,
+ nsHttpHeaderArray::eVarietyRequestOverride);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ LOG(("Setting Accept-Encoding: %s", mDictionaryAcceptEncodings.get()));
- LOG(("Setting Available-Dictionary: %s",
- PromiseFlatCString(dict->GetHash()).get()));
- rv = aRequest->SetHeader(nsHttp::Available_Dictionary, dict->GetHash(),
- false, nsHttpHeaderArray::eVarietyRequestOverride);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (!dict->GetId().IsEmpty()) {
- rv = aRequest->SetHeader(nsHttp::Dictionary_Id, dict->GetId(), false,
+ nsAutoCStringN<64> encodedHash = ":"_ns + aDict->GetHash() + ":"_ns;
+
+ LOG(("Setting Available-Dictionary: %s", encodedHash.get()));
+ rv = aRequest->SetHeader(nsHttp::Available_Dictionary, encodedHash, false,
nsHttpHeaderArray::eVarietyRequestOverride);
if (NS_FAILED(rv)) {
return rv;
}
- LOG(("Setting Dictionary-Id: %s",
- PromiseFlatCString(dict->GetId()).get()));
- }
- // Need to retain access to the dictionary until the request completes.
- // Note that this includes if the dictionary we offered gets replaced
- // by another request while we're waiting for a response; in that case we
- // need to read in a copy of the dictionary into memory before overwriting
- // it and store in dict temporarily.
- dict->InUse();
- aRequest->SetDictionary(dict);
+ if (!aDict->GetId().IsEmpty()) {
+ rv = aRequest->SetHeader(nsHttp::Dictionary_Id, aDict->GetId(), false,
+ nsHttpHeaderArray::eVarietyRequestOverride);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ LOG(("Setting Dictionary-Id: %s", aDict->GetId().get()));
+ }
+ // Need to retain access to the dictionary until the request completes.
+ // Note that this includes if the dictionary we offered gets replaced
+ // by another request while we're waiting for a response; in that case we
+ // need to read in a copy of the dictionary into memory before overwriting
+ // it and store in dict temporarily.
+ aRequest->SetDictionary(aDict);
+ } else {
+ rv = aRequest->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings,
+ false,
+ nsHttpHeaderArray::eVarietyRequestOverride);
+ }
} else {
- rv = aRequest->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings,
- false, nsHttpHeaderArray::eVarietyRequestOverride);
+ // We need to not override a previous setting of 'identity' (for range
+ // requests)
+ nsAutoCString encoding;
+ Unused << aRequest->GetHeader(nsHttp::Accept_Encoding, encoding);
+ if (!encoding.EqualsLiteral("identity")) {
+ rv = aRequest->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings,
+ false,
+ nsHttpHeaderArray::eVarietyRequestOverride);
+ }
}
+
return rv;
}
nsresult nsHttpHandler::AddStandardRequestHeaders(
- nsHttpRequestHead* request, bool isSecure, nsIURI* aURI,
+ nsHttpRequestHead* request, nsIURI* aURI,
ExtContentPolicyType aContentPolicyType, bool aShouldResistFingerprinting) {
nsresult rv;
@@ -751,15 +766,6 @@ nsresult nsHttpHandler::AddStandardRequestHeaders(
if (NS_FAILED(rv)) return rv;
}
- // Add the "Accept-Encoding" header
- if (isSecure) {
- rv = AddAcceptAndDictionaryHeaders(aURI, request);
- } else {
- rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings,
- false, nsHttpHeaderArray::eVarietyRequestDefault);
- }
- if (NS_FAILED(rv)) return rv;
-
// add the "Send Hint" header
if (mSafeHintEnabled || sParentalControlsEnabled) {
rv = request->SetHeader(nsHttp::Prefer, "safe"_ns, false,
@@ -769,19 +775,6 @@ nsresult nsHttpHandler::AddStandardRequestHeaders(
return NS_OK;
}
-nsresult nsHttpHandler::AddEncodingHeaders(nsHttpRequestHead* request,
- bool isSecure, nsIURI* aURI) {
- // Add the "Accept-Encoding" header and any dictionary headers
- nsresult rv;
- if (isSecure) {
- rv = AddAcceptAndDictionaryHeaders(aURI, request);
- } else {
- rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings,
- false, nsHttpHeaderArray::eVarietyRequestOverride);
- }
- return rv;
-}
-
nsresult nsHttpHandler::AddConnectionHeader(nsHttpRequestHead* request,
uint32_t caps) {
// RFC2616 section 19.6.2 states that the "Connection: keep-alive"
@@ -807,8 +800,10 @@ bool nsHttpHandler::IsAcceptableEncoding(const char* enc, bool isSecure) {
// continuing bad behavior.. so limit it to known x-* patterns
bool rv;
if (isSecure) {
- rv = nsHttp::FindToken(mHttpsAcceptEncodings.get(), enc, HTTP_LWS ",") !=
- nullptr;
+ // Should be a superset of mAcceptEncodings (unless someone messes with
+ // prefs)
+ rv = nsHttp::FindToken(mDictionaryAcceptEncodings.get(), enc,
+ HTTP_LWS ",") != nullptr;
} else {
rv = nsHttp::FindToken(mHttpAcceptEncodings.get(), enc, HTTP_LWS ",") !=
nullptr;
@@ -1487,22 +1482,25 @@ void nsHttpHandler::PrefsChanged(const char* pref) {
}
}
- if (PREF_CHANGED(HTTP_PREF("accept-encoding.secure"))) {
+ if (PREF_CHANGED(HTTP_PREF("accept-encoding.secure")) ||
+ PREF_CHANGED(HTTP_PREF("accept-encoding.dictionary"))) {
nsAutoCString acceptEncodings;
rv = Preferences::GetCString(HTTP_PREF("accept-encoding.secure"),
acceptEncodings);
if (NS_SUCCEEDED(rv)) {
rv = SetAcceptEncodings(acceptEncodings.get(), true, false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
- }
- if (PREF_CHANGED(HTTP_PREF("accept-encoding.dictionary"))) {
- nsAutoCString acceptDictionaryEncodings;
- rv = Preferences::GetCString(HTTP_PREF("accept-encoding.dictionary"),
- acceptDictionaryEncodings);
- if (NS_SUCCEEDED(rv)) {
- rv = SetAcceptEncodings(acceptDictionaryEncodings.get(), true, true);
+ // Since dictionary encodings are dependent on both accept-encoding.secure
+ // and accept-encoding.dictionary, update both if either changes (which is
+ // quite rare, so there's no real perf hit)
+ nsAutoCString acceptDictionaryEncodings;
+ rv = Preferences::GetCString(HTTP_PREF("accept-encoding.dictionary"),
+ acceptDictionaryEncodings);
+ if (NS_SUCCEEDED(rv) && !acceptDictionaryEncodings.IsEmpty()) {
+ acceptEncodings.Append(", "_ns);
+ acceptEncodings.Append(acceptDictionaryEncodings);
+ rv = SetAcceptEncodings(acceptEncodings.get(), true, true);
+ }
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h
@@ -118,13 +118,11 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
static already_AddRefed<nsHttpHandler> GetInstance();
[[nodiscard]] nsresult AddAcceptAndDictionaryHeaders(
- nsIURI* aURI, nsHttpRequestHead* aRequest);
+ nsIURI* aURI, nsHttpRequestHead* aRequest, bool aSecure,
+ RefPtr<DictionaryCacheEntry>& aDict);
[[nodiscard]] nsresult AddStandardRequestHeaders(
- nsHttpRequestHead*, bool isSecure, nsIURI* aURI,
- ExtContentPolicyType aContentPolicyType,
+ nsHttpRequestHead*, nsIURI* aURI, ExtContentPolicyType aContentPolicyType,
bool aShouldResistFingerprinting);
- [[nodiscard]] nsresult AddEncodingHeaders(nsHttpRequestHead* request,
- bool isSecure, nsIURI* aURI);
[[nodiscard]] nsresult AddConnectionHeader(nsHttpRequestHead*, uint32_t caps);
bool IsAcceptableEncoding(const char* encoding, bool isSecure);
diff --git a/netwerk/protocol/http/nsIHttpChannel.idl b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -10,8 +10,13 @@ interface nsIReferrerInfo;
%{C++
#include "GeckoProfiler.h"
+namespace mozilla::net {
+class DictionaryCacheEntry;
+}
%}
+[ptr] native DictionaryCacheEntry(mozilla::net::DictionaryCacheEntry);
+
native UniqueProfileChunkedBuffer(mozilla::UniquePtr<mozilla::ProfileChunkedBuffer>);
/**
@@ -508,4 +513,10 @@ interface nsIHttpChannel : nsIIdentChannel
* and user agent on the channel is outdated.
*/
[noscript, must_use] attribute boolean isUserAgentHeaderOutdated;
+ /**
+ * Dictionary for decompression, if any
+ */
+ [noscript] attribute DictionaryCacheEntry dictionary;
+
+
};
diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -1224,6 +1224,19 @@ nsViewSourceChannel::AsyncOnChannelRedirect(
return NS_OK;
}
+NS_IMETHODIMP
+nsViewSourceChannel::GetDictionary(
+ mozilla::net::DictionaryCacheEntry** aDictionary) {
+ *aDictionary = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsViewSourceChannel::SetDictionary(
+ mozilla::net::DictionaryCacheEntry* aDictionary) {
+ return NS_OK;
+}
+
// nsIInterfaceRequestor
NS_IMETHODIMP
diff --git a/netwerk/test/unit/xpcshell.toml b/netwerk/test/unit/xpcshell.toml
@@ -471,6 +471,8 @@ run-sequentially = ["true"] # httpd server
["test_cache2_nostore.js"]
+["test_cache2_compression_dictionary.js"]
+
["test_cache_204_response.js"]
["test_cache_jar.js"]