Skip to content

Commit 75d7bae

Browse files
committed
Bug 2013394: Suspend a channel before installing listeners if we're waiting on prefetch of a dictionary r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D281081
1 parent 902a9d9 commit 75d7bae

3 files changed

Lines changed: 50 additions & 18 deletions

File tree

‎netwerk/protocol/http/nsHttpChannel.cpp‎

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,7 +2424,8 @@ nsresult nsHttpChannel::CallOnStartRequest() {
24242424
(void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
24252425
// Note: doesn't handle multiple compressors: "dcb, gzip" or
24262426
// "gzip, dcb" (etc)
2427-
if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) {
2427+
if (contentEncoding.LowerCaseEqualsLiteral("dcb") ||
2428+
contentEncoding.LowerCaseEqualsLiteral("dcz")) {
24282429
LOG_DICTIONARIES(
24292430
("Still had %s encoding at CallOnStartRequest, converting",
24302431
contentEncoding.get()));
@@ -3654,10 +3655,11 @@ nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) {
36543655
mIsDictionaryCompressed = false;
36553656
nsAutoCString contentEncoding;
36563657
(void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
3657-
if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) {
3658+
if (contentEncoding.LowerCaseEqualsLiteral("dcb") ||
3659+
contentEncoding.LowerCaseEqualsLiteral("dcz")) {
36583660
mIsDictionaryCompressed = true;
3659-
} else if (contentEncoding.Find("dcb") != -1 ||
3660-
contentEncoding.Find("dcz") != -1) {
3661+
} else if (contentEncoding.LowerCaseFindASCII("dcb") != -1 ||
3662+
contentEncoding.LowerCaseFindASCII("dcz") != -1) {
36613663
// Reject responses that combine dcb/dcz with other encodings
36623664
// (e.g. "dcb, gzip" or "gzip, dcz"). We don't support chained
36633665
// dictionary compression with other compression methods.
@@ -3771,6 +3773,32 @@ nsresult nsHttpChannel::ContinueProcessNormal2(nsresult rv) {
37713773
if (NS_FAILED(rv)) CloseCacheEntry(true);
37723774
}
37733775

3776+
// CRITICAL: Check if dictionary is ready BEFORE creating decompressor.
3777+
// If we create the decompressor before the dictionary is ready, it will
3778+
// be created without the dictionary attached, causing decompression to fail.
3779+
// The dictionary prefetch callback (in PrepareToConnect) will call Resume()
3780+
// when ready, which will re-invoke ContinueProcessNormal2 via mCallOnResume.
3781+
if (mDictDecompress && mUsingDictionary && mShouldSuspendForDictionary &&
3782+
!mDictDecompress->DictionaryReady()) {
3783+
LOG_DICTIONARIES(
3784+
("nsHttpChannel::ContinueProcessNormal2 [this=%p] Suspending before "
3785+
"creating decompressor, waiting for dictionary",
3786+
this));
3787+
Suspend();
3788+
mSuspendedForDictionary = true;
3789+
// Set up callback to resume processing when dictionary loads
3790+
mCallOnResume = [](nsHttpChannel* self) {
3791+
return self->ContinueProcessNormal3();
3792+
};
3793+
return NS_OK;
3794+
}
3795+
3796+
return ContinueProcessNormal3();
3797+
}
3798+
3799+
nsresult nsHttpChannel::ContinueProcessNormal3() {
3800+
nsresult rv = NS_OK;
3801+
37743802
// Finish post-ParseDictionary work, must be done after waiting if Suspended
37753803
if (mCacheEntry && !LoadCacheEntryIsReadOnly()) {
37763804
if (mIsDictionaryCompressed || mDictSaving) {
@@ -3779,6 +3807,10 @@ nsresult nsHttpChannel::ContinueProcessNormal2(nsresult rv) {
37793807
if (NS_FAILED(rv)) {
37803808
LOG_DICTIONARIES(
37813809
("DoInstallCacheListener FAILED: %x", static_cast<uint32_t>(rv)));
3810+
// Cache entry is now corrupted - we set up headers with dcb/dcz
3811+
// Content-Encoding but failed to install the decompressor that would
3812+
// clear it. Doom the entry to prevent serving corrupted data.
3813+
CloseCacheEntry(true);
37823814
}
37833815
}
37843816
}
@@ -3809,18 +3841,6 @@ nsresult nsHttpChannel::ContinueProcessNormal2(nsresult rv) {
38093841
}
38103842
}
38113843

3812-
// If we don't have the entire dictionary yet, Suspend() the channel
3813-
// until the dictionary is in-memory.
3814-
if (mDictDecompress && mUsingDictionary && mShouldSuspendForDictionary &&
3815-
!mDictDecompress->DictionaryReady()) {
3816-
LOG(
3817-
("nsHttpChannel::ContinueProcessNormal [this=%p] Suspending the "
3818-
"transaction, waiting for dictionary",
3819-
this));
3820-
Suspend();
3821-
mSuspendedForDictionary = true;
3822-
}
3823-
38243844
rv = CallOnStartRequest();
38253845
if (NS_FAILED(rv)) return rv;
38263846

‎netwerk/protocol/http/nsHttpChannel.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ class nsHttpChannel final : public HttpBaseChannel,
367367
[[nodiscard]] nsresult ProcessNormal();
368368
[[nodiscard]] nsresult ContinueProcessNormal(nsresult);
369369
[[nodiscard]] nsresult ContinueProcessNormal2(nsresult);
370+
[[nodiscard]] nsresult ContinueProcessNormal3();
370371
void ProcessAltService(nsHttpConnectionInfo* aTransConnInfo = nullptr);
371372
bool ShouldBypassProcessNotModified();
372373
[[nodiscard]] nsresult ProcessNotModified(

‎netwerk/streamconv/converters/nsHTTPCompressConv.cpp‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ class BrotliWrapper {
6161
if (!httpchannel) {
6262
return false;
6363
}
64-
// XXX Wait for dictionary to be read into RAM!!
6564
if (NS_SUCCEEDED(httpchannel->GetDecompressDictionary(
6665
getter_AddRefs(mDictionary))) &&
6766
mDictionary) {
67+
// Critical: Dictionary must be fully loaded before use
68+
if (!mDictionary->DictionaryReady()) {
69+
DICTIONARY_LOG(("Brotli: dictionary not ready yet!"));
70+
MOZ_ASSERT(false, "Dictionary should be ready before decompression");
71+
return false;
72+
}
6873
size_t length = mDictionary->GetDictionary().length();
6974
DICTIONARY_LOG(("Brotli: dictionary %zu bytes", length));
7075
if (length > 0) {
@@ -111,12 +116,18 @@ class ZstdWrapper {
111116
if (aMode == nsHTTPCompressConv::HTTP_COMPRESS_ZSTD_DICTIONARY) {
112117
nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aRequest));
113118
if (httpchannel) {
114-
// XXX Wait for dictionary to be read into RAM!!
115119
if (NS_FAILED(httpchannel->GetDecompressDictionary(
116120
getter_AddRefs(mDictionary))) ||
117121
!mDictionary) {
118122
return;
119123
}
124+
// Critical: Dictionary must be fully loaded before use
125+
if (!mDictionary->DictionaryReady()) {
126+
DICTIONARY_LOG(("Zstd: dictionary not ready yet!"));
127+
MOZ_ASSERT(false, "Dictionary should be ready before decompression");
128+
mDictionary = nullptr;
129+
return;
130+
}
120131
length = mDictionary->GetDictionary().length();
121132
} else {
122133
// Can't decode without a dictionary

0 commit comments

Comments
 (0)