From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- netwerk/protocol/http/OpaqueResponseUtils.h | 216 ++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 netwerk/protocol/http/OpaqueResponseUtils.h (limited to 'netwerk/protocol/http/OpaqueResponseUtils.h') diff --git a/netwerk/protocol/http/OpaqueResponseUtils.h b/netwerk/protocol/http/OpaqueResponseUtils.h new file mode 100644 index 0000000000..7b37095b01 --- /dev/null +++ b/netwerk/protocol/http/OpaqueResponseUtils.h @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ + +/* 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_OpaqueResponseUtils_h +#define mozilla_net_OpaqueResponseUtils_h + +#include "ipc/EnumSerializer.h" +#include "mozilla/TimeStamp.h" +#include "nsIContentPolicy.h" +#include "nsIEncodedChannel.h" +#include "nsIStreamListener.h" +#include "nsUnknownDecoder.h" +#include "nsMimeTypes.h" +#include "nsIHttpChannel.h" + +#include "mozilla/Variant.h" +#include "mozilla/Logging.h" + +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsTArray.h" + +class nsIContentSniffer; + +namespace mozilla::dom { +class JSValidatorParent; +} + +namespace mozilla::ipc { +class Shmem; +} + +namespace mozilla::net { + +class HttpBaseChannel; +class nsHttpResponseHead; + +enum class OpaqueResponseBlockedReason : uint32_t { + ALLOWED_SAFE_LISTED, + ALLOWED_SAFE_LISTED_SPEC_BREAKING, + BLOCKED_BLOCKLISTED_NEVER_SNIFFED, + BLOCKED_206_AND_BLOCKLISTED, + BLOCKED_NOSNIFF_AND_EITHER_BLOCKLISTED_OR_TEXTPLAIN, + BLOCKED_SHOULD_SNIFF +}; + +enum class OpaqueResponseBlockedTelemetryReason : uint32_t { + MIME_NEVER_SNIFFED, + RESP_206_BLCLISTED, + NOSNIFF_BLC_OR_TEXTP, + RESP_206_NO_FIRST, + AFTER_SNIFF_MEDIA, + AFTER_SNIFF_NOSNIFF, + AFTER_SNIFF_STA_CODE, + AFTER_SNIFF_CT_FAIL, + MEDIA_NOT_INITIAL, + MEDIA_INCORRECT_RESP, + JS_VALIDATION_FAILED +}; + +enum class OpaqueResponse { Block, Allow, SniffCompressed, Sniff }; + +OpaqueResponseBlockedReason GetOpaqueResponseBlockedReason( + const nsACString& aContentType, uint16_t aStatus, bool aNoSniff); + +OpaqueResponseBlockedReason GetOpaqueResponseBlockedReason( + nsHttpResponseHead& aResponseHead); + +// Returns a tuple of (rangeStart, rangeEnd, rangeTotal) from the input range +// header string if succeed. +Result, nsresult> +ParseContentRangeHeaderString(const nsAutoCString& aRangeStr); + +bool IsFirstPartialResponse(nsHttpResponseHead& aResponseHead); + +LogModule* GetORBLog(); + +// Helper class to filter data for opaque responses destined for `Window.fetch`. +// See https://fetch.spec.whatwg.org/#concept-filtered-response-opaque. +class OpaqueResponseFilter final : public nsIStreamListener { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER; + + explicit OpaqueResponseFilter(nsIStreamListener* aNext); + + private: + virtual ~OpaqueResponseFilter() = default; + + nsCOMPtr mNext; +}; + +class OpaqueResponseBlocker final : public nsIStreamListener { + enum class State { Sniffing, Allowed, Blocked }; + + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER; + + OpaqueResponseBlocker(nsIStreamListener* aNext, HttpBaseChannel* aChannel, + const nsCString& aContentType, bool aNoSniff); + + bool IsSniffing() const; + void AllowResponse(); + void BlockResponse(HttpBaseChannel* aChannel, nsresult aStatus); + void FilterResponse(); + + nsresult EnsureOpaqueResponseIsAllowedAfterSniff(nsIRequest* aRequest); + + OpaqueResponse EnsureOpaqueResponseIsAllowedAfterJavaScriptValidation( + HttpBaseChannel* aChannel, bool aAllow); + + // The four possible results for validation. `JavaScript` and `JSON` are + // self-explanatory. `JavaScript` is the only successful result, in the sense + // that it will allow the opaque response, whereas `JSON` will block. `Other` + // is the case where validation fails, because the response is neither + // `JavaScript` nor `JSON`, but the framework itself works as intended. + // `Failure` implies that something has gone wrong, such as allocation, etc. + enum class ValidatorResult : uint32_t { JavaScript, JSON, Other, Failure }; + + private: + virtual ~OpaqueResponseBlocker() = default; + + nsresult ValidateJavaScript(HttpBaseChannel* aChannel, nsIURI* aURI, + nsILoadInfo* aLoadInfo); + + void ResolveAndProcessData(HttpBaseChannel* aChannel, bool aAllowed, + Maybe& aSharedData); + + void MaybeRunOnStopRequest(HttpBaseChannel* aChannel); + + nsCOMPtr mNext; + + const nsCString mContentType; + const bool mNoSniff; + bool mShouldFilter = false; + + State mState = State::Sniffing; + nsresult mStatus = NS_OK; + + TimeStamp mStartOfJavaScriptValidation; + + RefPtr mJSValidator; + + Maybe mPendingOnStopRequestStatus{Nothing()}; +}; + +class nsCompressedAudioVideoImageDetector : public nsUnknownDecoder { + const std::function mCallback; + + public: + nsCompressedAudioVideoImageDetector( + nsIStreamListener* aListener, + std::function&& aCallback) + : nsUnknownDecoder(aListener), mCallback(aCallback) {} + + protected: + virtual void DetermineContentType(nsIRequest* aRequest) override { + nsCOMPtr httpChannel = do_QueryInterface(aRequest); + if (!httpChannel) { + return; + } + + const char* testData = mBuffer; + uint32_t testDataLen = mBufferLen; + // Check if data are compressed. + nsAutoCString decodedData; + + // ConvertEncodedData is always called only on a single thread for each + // instance of an object. + nsresult rv = ConvertEncodedData(aRequest, mBuffer, mBufferLen); + if (NS_SUCCEEDED(rv)) { + MutexAutoLock lock(mMutex); + decodedData = mDecodedData; + } + if (!decodedData.IsEmpty()) { + testData = decodedData.get(); + testDataLen = std::min(decodedData.Length(), 512u); + } + + mCallback(httpChannel, (const uint8_t*)testData, testDataLen); + + nsAutoCString contentType; + rv = httpChannel->GetContentType(contentType); + + MutexAutoLock lock(mMutex); + if (!contentType.IsEmpty()) { + mContentType = contentType; + } else { + mContentType = UNKNOWN_CONTENT_TYPE; + } + + nsCOMPtr encodedChannel = do_QueryInterface(httpChannel); + if (encodedChannel) { + encodedChannel->SetHasContentDecompressed(true); + } + } +}; +} // namespace mozilla::net + +namespace IPC { +template <> +struct ParamTraits + : public ContiguousEnumSerializerInclusive< + mozilla::net::OpaqueResponseBlocker::ValidatorResult, + mozilla::net::OpaqueResponseBlocker::ValidatorResult::JavaScript, + mozilla::net::OpaqueResponseBlocker::ValidatorResult::Failure> {}; +} // namespace IPC + +#endif // mozilla_net_OpaqueResponseUtils_h -- cgit v1.2.3