diff --git a/netwerk/protocol/http/nsHttpHeaderArray.cpp b/netwerk/protocol/http/nsHttpHeaderArray.cpp index 5fb5d18105b3..e368bfe410c3 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.cpp +++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp @@ -231,6 +231,10 @@ void nsHttpHeaderArray::ClearHeader(const nsHttpAtom& header) { } } +void nsHttpHeaderArray::PurgeHeaderEntries(const nsHttpAtom& header) { + mHeaders.RemoveElementsBy( + [header](const auto& entry) { return (entry.header == header); }); +} const char* nsHttpHeaderArray::PeekHeader(const nsHttpAtom& header) const { const nsEntry* entry = nullptr; LookupEntry(header, &entry); @@ -444,12 +448,16 @@ void nsHttpHeaderArray::FlattenOriginalHeader(nsACString& buf) { } } -const char* nsHttpHeaderArray::PeekHeaderAt( - uint32_t index, nsHttpAtom& header, nsACString& headerNameOriginal) const { +const char* nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom& header, + nsACString& headerNameOriginal, + HeaderVariety& variety, + nsACString& val) const { const nsEntry& entry = mHeaders[index]; header = entry.header; headerNameOriginal = entry.headerNameOriginal; + variety = entry.variety; + val = entry.value; return entry.value.get(); } diff --git a/netwerk/protocol/http/nsHttpHeaderArray.h b/netwerk/protocol/http/nsHttpHeaderArray.h index ac0f433281d4..b1e978a604e8 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.h +++ b/netwerk/protocol/http/nsHttpHeaderArray.h @@ -82,6 +82,8 @@ class nsHttpHeaderArray { nsIHttpHeaderVisitor* aVisitor); void ClearHeader(const nsHttpAtom& h); + void PurgeHeaderEntries(const nsHttpAtom& header); + // Find the location of the given header value, or null if none exists. const char* FindHeaderValue(const nsHttpAtom& header, const char* value) const { @@ -117,7 +119,8 @@ class nsHttpHeaderArray { uint32_t Count() const { return mHeaders.Length(); } const char* PeekHeaderAt(uint32_t i, nsHttpAtom& header, - nsACString& headerNameOriginal) const; + nsACString& headerNameOriginal, + HeaderVariety& variety, nsACString& val) const; void Clear(); diff --git a/netwerk/protocol/http/nsHttpResponseHead.cpp b/netwerk/protocol/http/nsHttpResponseHead.cpp index 9758aaf9b01b..b4656234cb9b 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -163,12 +163,10 @@ nsresult nsHttpResponseHead::SetHeader(const nsHttpAtom& hdr, return SetHeader_locked(hdr, ""_ns, val, merge); } -nsresult nsHttpResponseHead::SetHeader_locked(const nsHttpAtom& atom, - const nsACString& hdr, - const nsACString& val, - bool merge) { - nsresult rv = mHeaders.SetHeader(atom, hdr, val, merge, - nsHttpHeaderArray::eVarietyResponse); +nsresult nsHttpResponseHead::SetHeader_locked( + const nsHttpAtom& atom, const nsACString& hdr, const nsACString& val, + bool merge, const nsHttpHeaderArray::HeaderVariety& variety) { + nsresult rv = mHeaders.SetHeader(atom, hdr, val, merge, variety); if (NS_FAILED(rv)) return rv; // respond to changes in these headers. we need to reparse the entire @@ -893,48 +891,95 @@ bool nsHttpResponseHead::ExpiresInPast_locked() const { NS_SUCCEEDED(GetDateValue_locked(&dateVal)) && expiresVal < dateVal; } +void nsHttpResponseHead::UpdateOriginalHeaders(nsHttpResponseHead* aOther) { + mRecursiveMutex.AssertCurrentThreadIn(); + aOther->mRecursiveMutex.AssertCurrentThreadIn(); + + uint32_t i, count = aOther->mHeaders.Count(); + + // container to maintain a list of entries purged + nsTHashSet purgedEntries; + + for (i = 0; i < count; ++i) { + nsHttpAtom header; + nsHttpHeaderArray::HeaderVariety variety; + nsAutoCString headerNameOriginal; + nsAutoCString val; + + if (!aOther->mHeaders.PeekHeaderAt(i, header, headerNameOriginal, variety, + val)) { + continue; + } + + if (CanIgnoreResponseHeaderTypes(header) || + !IsOriginalResponseHeader(variety)) { + continue; + } + + // remove old response header entries as we have received updated + // response headers from 304/206 response + if (purgedEntries.EnsureInserted(header.val())) { + mHeaders.PurgeHeaderEntries(header); + } + + DebugOnly rv = + mHeaders.SetHeaderFromNet(header, headerNameOriginal, val, true); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } +} + +bool nsHttpResponseHead::CanIgnoreResponseHeaderTypes( + const nsHttpAtom& header) const { + return (header == nsHttp::Connection || header == nsHttp::Proxy_Connection || + header == nsHttp::Keep_Alive || + header == nsHttp::Proxy_Authenticate || + header == nsHttp::Proxy_Authorization || + // not a response header! + header == nsHttp::TE || header == nsHttp::Trailer || + header == nsHttp::Transfer_Encoding || header == nsHttp::Upgrade || + // Ignore any non-modifiable headers... + header == nsHttp::Content_Location || header == nsHttp::Content_MD5 || + header == nsHttp::ETag || + // Assume Cache-Control: "no-transform" + header == nsHttp::Content_Encoding || + header == nsHttp::Content_Range || header == nsHttp::Content_Type || + // Ignore wacky headers too... + // this one is for MS servers that send "Content-Length: 0" + // on 304 responses + header == nsHttp::Content_Length); +} + void nsHttpResponseHead::UpdateHeaders(nsHttpResponseHead* aOther) { LOG(("nsHttpResponseHead::UpdateHeaders [this=%p]\n", this)); RecursiveMutexAutoLock monitor(mRecursiveMutex); RecursiveMutexAutoLock monitorOther(aOther->mRecursiveMutex); + UpdateOriginalHeaders(aOther); + uint32_t i, count = aOther->mHeaders.Count(); + for (i = 0; i < count; ++i) { nsHttpAtom header; nsAutoCString headerNameOriginal; - - if (!aOther->mHeaders.PeekHeaderAt(i, header, headerNameOriginal)) { - continue; - } - + nsHttpHeaderArray::HeaderVariety variety; nsAutoCString val; - if (NS_FAILED(aOther->GetHeader(header, val))) { + + if (!aOther->mHeaders.PeekHeaderAt(i, header, headerNameOriginal, variety, + val)) { continue; } - // Ignore any hop-by-hop headers... - if (header == nsHttp::Connection || header == nsHttp::Proxy_Connection || - header == nsHttp::Keep_Alive || header == nsHttp::Proxy_Authenticate || - header == nsHttp::Proxy_Authorization || // not a response header! - header == nsHttp::TE || header == nsHttp::Trailer || - header == nsHttp::Transfer_Encoding || header == nsHttp::Upgrade || - // Ignore any non-modifiable headers... - header == nsHttp::Content_Location || header == nsHttp::Content_MD5 || - header == nsHttp::ETag || - // Assume Cache-Control: "no-transform" - header == nsHttp::Content_Encoding || header == nsHttp::Content_Range || - header == nsHttp::Content_Type || - // Ignore wacky headers too... - // this one is for MS servers that send "Content-Length: 0" - // on 304 responses - header == nsHttp::Content_Length) { + if (CanIgnoreResponseHeaderTypes(header)) { LOG(("ignoring response header [%s: %s]\n", header.get(), val.get())); } else { LOG(("new response header [%s: %s]\n", header.get(), val.get())); - // overwrite the current header value with the new value... + if (NS_FAILED(aOther->GetHeader(header, val))) { + continue; + } DebugOnly rv = + // overwrite the current header value with the new value... SetHeader_locked(header, headerNameOriginal, val); MOZ_ASSERT(NS_SUCCEEDED(rv)); } diff --git a/netwerk/protocol/http/nsHttpResponseHead.h b/netwerk/protocol/http/nsHttpResponseHead.h index e68a3340d659..39610cc35c0b 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.h +++ b/netwerk/protocol/http/nsHttpResponseHead.h @@ -147,10 +147,11 @@ class nsHttpResponseHead { bool GetContentTypeOptionsHeader(nsACString& aOutput) const; private: - [[nodiscard]] nsresult SetHeader_locked(const nsHttpAtom& atom, - const nsACString& h, - const nsACString& v, bool m = false) - MOZ_REQUIRES(mRecursiveMutex); + [[nodiscard]] nsresult SetHeader_locked( + const nsHttpAtom& atom, const nsACString& h, const nsACString& v, + bool m = false, + const nsHttpHeaderArray::HeaderVariety& variety = + nsHttpHeaderArray::eVarietyResponse) MOZ_REQUIRES(mRecursiveMutex); void AssignDefaultStatusText() MOZ_REQUIRES(mRecursiveMutex); void ParseVersion(const char*) MOZ_REQUIRES(mRecursiveMutex); void ParseCacheControl(const char*) MOZ_REQUIRES(mRecursiveMutex); @@ -198,6 +199,19 @@ class nsHttpResponseHead { return mHasCacheControl ? mCacheControlNoCache : mPragmaNoCache; } + // update original response headers + void UpdateOriginalHeaders(nsHttpResponseHead* aOther); + + // headers that can be ignored for a 304/206 response + bool CanIgnoreResponseHeaderTypes(const nsHttpAtom& header) const; + + bool IsOriginalResponseHeader( + const nsHttpHeaderArray::HeaderVariety variety) const { + return ( + (variety == nsHttpHeaderArray::eVarietyResponseNetOriginal) || + (variety == nsHttpHeaderArray::eVarietyResponseNetOriginalAndResponse)); + } + private: // All members must be copy-constructable and assignable nsHttpHeaderArray mHeaders MOZ_GUARDED_BY(mRecursiveMutex);