diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/fetch/InternalResponse.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/fetch/InternalResponse.cpp')
-rw-r--r-- | dom/fetch/InternalResponse.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp new file mode 100644 index 0000000000..342a88eeeb --- /dev/null +++ b/dom/fetch/InternalResponse.cpp @@ -0,0 +1,361 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 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/. */ + +#include "InternalResponse.h" + +#include "mozilla/Assertions.h" +#include "mozilla/RefPtr.h" +#include "mozilla/dom/FetchTypes.h" +#include "mozilla/dom/InternalHeaders.h" +#include "mozilla/dom/cache/CacheTypes.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "mozilla/ipc/IPCStreamUtils.h" +#include "mozilla/RandomNum.h" +#include "mozilla/RemoteLazyInputStreamStorage.h" +#include "nsIRandomGenerator.h" +#include "nsStreamUtils.h" + +namespace mozilla::dom { + +namespace { + +// Const variable for generate padding size +// XXX This will be tweaked to something more meaningful in Bug 1383656. +const uint32_t kMaxRandomNumber = 102400; + +nsCOMPtr<nsIInputStream> TakeStreamFromStorage( + const BodyStreamVariant& aVariant, int64_t aBodySize) { + MOZ_ASSERT(aVariant.type() == BodyStreamVariant::TParentToParentStream); + const auto& uuid = aVariant.get_ParentToParentStream().uuid(); + + auto storageOrErr = RemoteLazyInputStreamStorage::Get(); + MOZ_ASSERT(storageOrErr.isOk()); + auto storage = storageOrErr.unwrap(); + auto stream = storage->ForgetStream(uuid); + MOZ_ASSERT(stream); + + return stream; +} + +} // namespace + +InternalResponse::InternalResponse(uint16_t aStatus, + const nsACString& aStatusText, + RequestCredentials aCredentialsMode) + : mType(ResponseType::Default), + mStatus(aStatus), + mStatusText(aStatusText), + mHeaders(new InternalHeaders(HeadersGuardEnum::Response)), + mBodySize(UNKNOWN_BODY_SIZE), + mPaddingSize(UNKNOWN_PADDING_SIZE), + mErrorCode(NS_OK), + mCredentialsMode(aCredentialsMode) {} + +/* static */ RefPtr<InternalResponse> InternalResponse::FromIPC( + const IPCInternalResponse& aIPCResponse) { + if (aIPCResponse.type() == ResponseType::Error) { + return InternalResponse::NetworkError(aIPCResponse.errorCode()); + } + + RefPtr<InternalResponse> response = + new InternalResponse(aIPCResponse.status(), aIPCResponse.statusText()); + + response->SetURLList(aIPCResponse.urlList()); + response->mHeaders = + new InternalHeaders(aIPCResponse.headers(), aIPCResponse.headersGuard()); + + if (aIPCResponse.body()) { + auto bodySize = aIPCResponse.bodySize(); + nsCOMPtr<nsIInputStream> body = + TakeStreamFromStorage(*aIPCResponse.body(), bodySize); + response->SetBody(body, bodySize); + } + + response->SetAlternativeDataType(aIPCResponse.alternativeDataType()); + + if (aIPCResponse.alternativeBody()) { + nsCOMPtr<nsIInputStream> alternativeBody = TakeStreamFromStorage( + *aIPCResponse.alternativeBody(), UNKNOWN_BODY_SIZE); + response->SetAlternativeBody(alternativeBody); + } + + response->InitChannelInfo(aIPCResponse.channelInfo()); + + if (aIPCResponse.principalInfo()) { + response->SetPrincipalInfo(MakeUnique<mozilla::ipc::PrincipalInfo>( + aIPCResponse.principalInfo().ref())); + } + + switch (aIPCResponse.type()) { + case ResponseType::Basic: + response = response->BasicResponse(); + break; + case ResponseType::Cors: + response = response->CORSResponse(); + break; + case ResponseType::Default: + break; + case ResponseType::Opaque: + response = response->OpaqueResponse(); + break; + case ResponseType::Opaqueredirect: + response = response->OpaqueRedirectResponse(); + break; + default: + MOZ_CRASH("Unexpected ResponseType!"); + } + + MOZ_ASSERT(response); + + return response; +} + +InternalResponse::~InternalResponse() = default; + +void InternalResponse::ToIPC( + IPCInternalResponse* aIPCResponse, mozilla::ipc::PBackgroundChild* aManager, + UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoBodyStream, + UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoAlternativeBodyStream) { + nsTArray<HeadersEntry> headers; + HeadersGuardEnum headersGuard; + UnfilteredHeaders()->ToIPC(headers, headersGuard); + + Maybe<mozilla::ipc::PrincipalInfo> principalInfo = + mPrincipalInfo ? Some(*mPrincipalInfo) : Nothing(); + + // Note: all the arguments are copied rather than moved, which would be more + // efficient, because there's no move-friendly constructor generated. + *aIPCResponse = + IPCInternalResponse(mType, GetUnfilteredURLList(), GetUnfilteredStatus(), + GetUnfilteredStatusText(), headersGuard, headers, + Nothing(), static_cast<uint64_t>(UNKNOWN_BODY_SIZE), + mErrorCode, GetAlternativeDataType(), Nothing(), + mChannelInfo.AsIPCChannelInfo(), principalInfo); + + nsCOMPtr<nsIInputStream> body; + int64_t bodySize; + GetUnfilteredBody(getter_AddRefs(body), &bodySize); + + if (body) { + aIPCResponse->body().emplace(ChildToParentStream()); + aIPCResponse->bodySize() = bodySize; + + aAutoBodyStream.reset(new mozilla::ipc::AutoIPCStream( + aIPCResponse->body()->get_ChildToParentStream().stream())); + DebugOnly<bool> ok = aAutoBodyStream->Serialize(body, aManager); + MOZ_ASSERT(ok); + } + + nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody(); + if (alternativeBody) { + aIPCResponse->alternativeBody().emplace(ChildToParentStream()); + + aAutoAlternativeBodyStream.reset(new mozilla::ipc::AutoIPCStream( + aIPCResponse->alternativeBody()->get_ChildToParentStream().stream())); + DebugOnly<bool> ok = + aAutoAlternativeBodyStream->Serialize(alternativeBody, aManager); + MOZ_ASSERT(ok); + } +} + +already_AddRefed<InternalResponse> InternalResponse::Clone( + CloneType aCloneType) { + RefPtr<InternalResponse> clone = CreateIncompleteCopy(); + + clone->mHeaders = new InternalHeaders(*mHeaders); + + // Make sure the clone response will have the same padding size. + clone->mPaddingInfo = mPaddingInfo; + clone->mPaddingSize = mPaddingSize; + + clone->mCacheInfoChannel = mCacheInfoChannel; + + if (mWrappedResponse) { + clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType); + MOZ_ASSERT(!mBody); + return clone.forget(); + } + + if (!mBody || aCloneType == eDontCloneInputStream) { + return clone.forget(); + } + + nsCOMPtr<nsIInputStream> clonedBody; + nsCOMPtr<nsIInputStream> replacementBody; + + nsresult rv = NS_CloneInputStream(mBody, getter_AddRefs(clonedBody), + getter_AddRefs(replacementBody)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + clone->mBody.swap(clonedBody); + if (replacementBody) { + mBody.swap(replacementBody); + } + + return clone.forget(); +} + +already_AddRefed<InternalResponse> InternalResponse::BasicResponse() { + MOZ_ASSERT(!mWrappedResponse, + "Can't BasicResponse a already wrapped response"); + RefPtr<InternalResponse> basic = CreateIncompleteCopy(); + basic->mType = ResponseType::Basic; + basic->mHeaders = InternalHeaders::BasicHeaders(Headers()); + basic->mWrappedResponse = this; + return basic.forget(); +} + +already_AddRefed<InternalResponse> InternalResponse::CORSResponse() { + MOZ_ASSERT(!mWrappedResponse, + "Can't CORSResponse a already wrapped response"); + RefPtr<InternalResponse> cors = CreateIncompleteCopy(); + cors->mType = ResponseType::Cors; + cors->mHeaders = InternalHeaders::CORSHeaders(Headers(), mCredentialsMode); + cors->mWrappedResponse = this; + return cors.forget(); +} + +uint32_t InternalResponse::GetPaddingInfo() { + // If it's an opaque response, the paddingInfo should be generated only when + // paddingSize is unknown size. + // If it's not, the paddingInfo should be nothing and the paddingSize should + // be unknown size. + MOZ_DIAGNOSTIC_ASSERT( + (mType == ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE && + mPaddingInfo.isSome()) || + (mType == ResponseType::Opaque && mPaddingSize != UNKNOWN_PADDING_SIZE && + mPaddingInfo.isNothing()) || + (mType != ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE && + mPaddingInfo.isNothing())); + return mPaddingInfo.isSome() ? mPaddingInfo.ref() : 0; +} + +nsresult InternalResponse::GeneratePaddingInfo() { + MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque); + MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE); + + // Utilize random generator to generator a random number + nsresult rv; + uint32_t randomNumber = 0; + nsCOMPtr<nsIRandomGenerator> randomGenerator = + do_GetService("@mozilla.org/security/random-generator;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + Maybe<uint64_t> maybeRandomNum = RandomUint64(); + if (maybeRandomNum.isSome()) { + mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber)); + return NS_OK; + } + return rv; + } + + MOZ_DIAGNOSTIC_ASSERT(randomGenerator); + + uint8_t* buffer; + rv = randomGenerator->GenerateRandomBytes(sizeof(randomNumber), &buffer); + if (NS_WARN_IF(NS_FAILED(rv))) { + Maybe<uint64_t> maybeRandomNum = RandomUint64(); + if (maybeRandomNum.isSome()) { + mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber)); + return NS_OK; + } + return rv; + } + + memcpy(&randomNumber, buffer, sizeof(randomNumber)); + free(buffer); + + mPaddingInfo.emplace(randomNumber % kMaxRandomNumber); + + return rv; +} + +int64_t InternalResponse::GetPaddingSize() { + // We initialize padding size to an unknown size (-1). After cached, we only + // pad opaque response. Opaque response's padding size might be unknown before + // cached. + MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque || + mPaddingSize == UNKNOWN_PADDING_SIZE); + MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE || + mPaddingSize >= 0); + + return mPaddingSize; +} + +void InternalResponse::SetPaddingSize(int64_t aPaddingSize) { + // We should only pad the opaque response. + MOZ_DIAGNOSTIC_ASSERT( + (mType == ResponseType::Opaque) != + (aPaddingSize == InternalResponse::UNKNOWN_PADDING_SIZE)); + MOZ_DIAGNOSTIC_ASSERT(aPaddingSize == UNKNOWN_PADDING_SIZE || + aPaddingSize >= 0); + + mPaddingSize = aPaddingSize; +} + +void InternalResponse::SetPrincipalInfo( + UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo) { + mPrincipalInfo = std::move(aPrincipalInfo); +} + +LoadTainting InternalResponse::GetTainting() const { + switch (mType) { + case ResponseType::Cors: + return LoadTainting::CORS; + case ResponseType::Opaque: + return LoadTainting::Opaque; + default: + return LoadTainting::Basic; + } +} + +already_AddRefed<InternalResponse> InternalResponse::Unfiltered() { + RefPtr<InternalResponse> ref = mWrappedResponse; + if (!ref) { + ref = this; + } + return ref.forget(); +} + +already_AddRefed<InternalResponse> InternalResponse::OpaqueResponse() { + MOZ_ASSERT(!mWrappedResponse, + "Can't OpaqueResponse a already wrapped response"); + RefPtr<InternalResponse> response = new InternalResponse(0, ""_ns); + response->mType = ResponseType::Opaque; + response->mChannelInfo = mChannelInfo; + if (mPrincipalInfo) { + response->mPrincipalInfo = + MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo); + } + response->mWrappedResponse = this; + return response.forget(); +} + +already_AddRefed<InternalResponse> InternalResponse::OpaqueRedirectResponse() { + MOZ_ASSERT(!mWrappedResponse, + "Can't OpaqueRedirectResponse a already wrapped response"); + MOZ_ASSERT(!mURLList.IsEmpty(), + "URLList should not be emtpy for internalResponse"); + RefPtr<InternalResponse> response = OpaqueResponse(); + response->mType = ResponseType::Opaqueredirect; + response->mURLList = mURLList.Clone(); + return response.forget(); +} + +already_AddRefed<InternalResponse> InternalResponse::CreateIncompleteCopy() { + RefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText); + copy->mType = mType; + copy->mURLList = mURLList.Clone(); + copy->mChannelInfo = mChannelInfo; + if (mPrincipalInfo) { + copy->mPrincipalInfo = + MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo); + } + return copy.forget(); +} + +} // namespace mozilla::dom |