summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/OpaqueResponseUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/http/OpaqueResponseUtils.h')
-rw-r--r--netwerk/protocol/http/OpaqueResponseUtils.h210
1 files changed, 210 insertions, 0 deletions
diff --git a/netwerk/protocol/http/OpaqueResponseUtils.h b/netwerk/protocol/http/OpaqueResponseUtils.h
new file mode 100644
index 0000000000..021a0d92c4
--- /dev/null
+++ b/netwerk/protocol/http/OpaqueResponseUtils.h
@@ -0,0 +1,210 @@
+/* -*- 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 "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<std::tuple<int64_t, int64_t, int64_t>, 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<nsIStreamListener> 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<ipc::Shmem>& aSharedData);
+
+ void MaybeRunOnStopRequest(HttpBaseChannel* aChannel);
+
+ nsCOMPtr<nsIStreamListener> mNext;
+
+ const nsCString mContentType;
+ const bool mNoSniff;
+ bool mShouldFilter = false;
+
+ State mState = State::Sniffing;
+ nsresult mStatus = NS_OK;
+
+ TimeStamp mStartOfJavaScriptValidation;
+
+ RefPtr<dom::JSValidatorParent> mJSValidator;
+
+ Maybe<nsresult> mPendingOnStopRequestStatus{Nothing()};
+};
+
+class nsCompressedAudioVideoImageDetector : public nsUnknownDecoder {
+ const std::function<void(void*, const uint8_t*, uint32_t)> mCallback;
+
+ public:
+ nsCompressedAudioVideoImageDetector(
+ nsIStreamListener* aListener,
+ std::function<void(void*, const uint8_t*, uint32_t)>&& aCallback)
+ : nsUnknownDecoder(aListener), mCallback(aCallback) {}
+
+ protected:
+ virtual void DetermineContentType(nsIRequest* aRequest) override {
+ nsCOMPtr<nsIHttpChannel> 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<uint32_t>(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;
+ }
+ }
+};
+} // namespace mozilla::net
+
+namespace IPC {
+template <>
+struct ParamTraits<mozilla::net::OpaqueResponseBlocker::ValidatorResult>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::net::OpaqueResponseBlocker::ValidatorResult,
+ mozilla::net::OpaqueResponseBlocker::ValidatorResult::JavaScript,
+ mozilla::net::OpaqueResponseBlocker::ValidatorResult::Failure> {};
+} // namespace IPC
+
+#endif // mozilla_net_OpaqueResponseUtils_h