зеркало из https://github.com/mozilla/gecko-dev.git
217 строки
6.5 KiB
C++
217 строки
6.5 KiB
C++
/* -*- 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<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<mozilla::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;
|
|
}
|
|
|
|
nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
|
|
if (encodedChannel) {
|
|
encodedChannel->SetHasContentDecompressed(true);
|
|
}
|
|
}
|
|
};
|
|
} // 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
|