summaryrefslogtreecommitdiffstats
path: root/dom/fetch/InternalResponse.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/fetch/InternalResponse.cpp')
-rw-r--r--dom/fetch/InternalResponse.cpp454
1 files changed, 454 insertions, 0 deletions
diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp
new file mode 100644
index 0000000000..1603d6e4cb
--- /dev/null
+++ b/dom/fetch/InternalResponse.cpp
@@ -0,0 +1,454 @@
+/* -*- 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/FetchStreamUtils.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;
+
+} // 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),
+ mCloned(false) {}
+
+/* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPC(
+ const ParentToParentInternalResponse& aIPCResponse) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ return FromIPCTemplate(aIPCResponse);
+}
+
+/* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPC(
+ const ParentToChildInternalResponse& aIPCResponse) {
+ MOZ_ASSERT(XRE_IsContentProcess());
+ return FromIPCTemplate(aIPCResponse);
+}
+
+template <typename T>
+/* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPCTemplate(
+ const T& aIPCResponse) {
+ if (aIPCResponse.metadata().type() == ResponseType::Error) {
+ return InternalResponse::NetworkError(aIPCResponse.metadata().errorCode());
+ }
+
+ SafeRefPtr<InternalResponse> response = MakeSafeRefPtr<InternalResponse>(
+ aIPCResponse.metadata().status(), aIPCResponse.metadata().statusText());
+
+ response->SetURLList(aIPCResponse.metadata().urlList());
+ response->mHeaders =
+ new InternalHeaders(aIPCResponse.metadata().headers(),
+ aIPCResponse.metadata().headersGuard());
+
+ if (aIPCResponse.body()) {
+ auto bodySize = aIPCResponse.bodySize();
+ auto body = ToInputStream(*aIPCResponse.body());
+ response->SetBody(body.get(), bodySize);
+ }
+
+ response->SetAlternativeDataType(
+ aIPCResponse.metadata().alternativeDataType());
+
+ if (aIPCResponse.alternativeBody()) {
+ auto alternativeBody = ToInputStream(*aIPCResponse.alternativeBody());
+ response->SetAlternativeBody(alternativeBody.get());
+ }
+
+ response->InitChannelInfo(aIPCResponse.metadata().securityInfo());
+
+ if (aIPCResponse.metadata().principalInfo()) {
+ response->SetPrincipalInfo(MakeUnique<mozilla::ipc::PrincipalInfo>(
+ aIPCResponse.metadata().principalInfo().ref()));
+ }
+
+ nsAutoCString bodyBlobURISpec(aIPCResponse.metadata().bodyBlobURISpec());
+ response->SetBodyBlobURISpec(bodyBlobURISpec);
+ nsAutoString bodyLocalPath(aIPCResponse.metadata().bodyLocalPath());
+ response->SetBodyLocalPath(bodyLocalPath);
+
+ response->mCredentialsMode = aIPCResponse.metadata().credentialsMode();
+
+ switch (aIPCResponse.metadata().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;
+
+InternalResponseMetadata InternalResponse::GetMetadata() {
+ nsTArray<HeadersEntry> headers;
+ HeadersGuardEnum headersGuard;
+ UnfilteredHeaders()->ToIPC(headers, headersGuard);
+
+ Maybe<mozilla::ipc::PrincipalInfo> principalInfo =
+ mPrincipalInfo ? Some(*mPrincipalInfo) : Nothing();
+
+ nsAutoCString bodyBlobURISpec(BodyBlobURISpec());
+ nsAutoString bodyLocalPath(BodyLocalPath());
+
+ // Note: all the arguments are copied rather than moved, which would be more
+ // efficient, because there's no move-friendly constructor generated.
+ nsCOMPtr<nsITransportSecurityInfo> securityInfo(mChannelInfo.SecurityInfo());
+ return InternalResponseMetadata(
+ mType, GetUnfilteredURLList(), GetUnfilteredStatus(),
+ GetUnfilteredStatusText(), headersGuard, headers, mErrorCode,
+ GetAlternativeDataType(), securityInfo, principalInfo, bodyBlobURISpec,
+ bodyLocalPath, GetCredentialsMode());
+}
+
+void InternalResponse::ToChildToParentInternalResponse(
+ ChildToParentInternalResponse* aIPCResponse,
+ mozilla::ipc::PBackgroundChild* aManager) {
+ *aIPCResponse = ChildToParentInternalResponse(GetMetadata(), Nothing(),
+ UNKNOWN_BODY_SIZE, Nothing());
+
+ nsCOMPtr<nsIInputStream> body;
+ int64_t bodySize;
+ GetUnfilteredBody(getter_AddRefs(body), &bodySize);
+
+ if (body) {
+ aIPCResponse->body().emplace(ChildToParentStream());
+ aIPCResponse->bodySize() = bodySize;
+
+ DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream(
+ body.forget(), aIPCResponse->body()->stream(), /* aAllowLazy */ false);
+ MOZ_ASSERT(ok);
+ }
+
+ nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
+ if (alternativeBody) {
+ aIPCResponse->alternativeBody().emplace(ChildToParentStream());
+
+ DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream(
+ alternativeBody.forget(), aIPCResponse->alternativeBody()->stream(),
+ /* aAllowLazy */ false);
+ MOZ_ASSERT(ok);
+ }
+}
+
+ParentToParentInternalResponse
+InternalResponse::ToParentToParentInternalResponse() {
+ ParentToParentInternalResponse result(GetMetadata(), Nothing(),
+ UNKNOWN_BODY_SIZE, Nothing());
+
+ nsCOMPtr<nsIInputStream> body;
+ int64_t bodySize;
+ GetUnfilteredBody(getter_AddRefs(body), &bodySize);
+
+ if (body) {
+ result.body() = Some(ToParentToParentStream(WrapNotNull(body), bodySize));
+ result.bodySize() = bodySize;
+ }
+
+ nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
+ if (alternativeBody) {
+ result.alternativeBody() = Some(ToParentToParentStream(
+ WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE));
+ }
+
+ return result;
+}
+
+ParentToChildInternalResponse InternalResponse::ToParentToChildInternalResponse(
+ NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
+ ParentToChildInternalResponse result(GetMetadata(), Nothing(),
+ UNKNOWN_BODY_SIZE, Nothing());
+
+ nsCOMPtr<nsIInputStream> body;
+ int64_t bodySize;
+ GetUnfilteredBody(getter_AddRefs(body), &bodySize);
+
+ if (body) {
+ ParentToChildStream bodyStream = ToParentToChildStream(
+ WrapNotNull(body), bodySize, aBackgroundParent, mSerializeAsLazy);
+ // The body stream can fail to serialize as an IPCStream. In the case, the
+ // IPCStream's type would be T__None. Don't set up IPCInternalResponse's
+ // body with the failed IPCStream.
+ if (mSerializeAsLazy || bodyStream.get_IPCStream().stream().type() !=
+ mozilla::ipc::InputStreamParams::T__None) {
+ result.body() = Some(bodyStream);
+ result.bodySize() = bodySize;
+ }
+ }
+
+ nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
+ if (alternativeBody) {
+ ParentToChildStream alterBodyStream =
+ ToParentToChildStream(WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE,
+ aBackgroundParent, mSerializeAsLazy);
+ // The body stream can fail to serialize as an IPCStream. In the case, the
+ // IPCStream's type would be T__None. Don't set up IPCInternalResponse's
+ // body with the failed IPCStream.
+ if (mSerializeAsLazy || alterBodyStream.get_IPCStream().stream().type() !=
+ mozilla::ipc::InputStreamParams::T__None) {
+ result.alternativeBody() = Some(alterBodyStream);
+ }
+ }
+
+ return result;
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::Clone(CloneType aCloneType) {
+ SafeRefPtr<InternalResponse> clone = CreateIncompleteCopy();
+ clone->mCloned = (mCloned = true);
+
+ 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;
+ clone->mCredentialsMode = mCredentialsMode;
+
+ if (mWrappedResponse) {
+ clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
+ MOZ_ASSERT(!mBody);
+ return clone;
+ }
+
+ if (!mBody || aCloneType == eDontCloneInputStream) {
+ return clone;
+ }
+
+ 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;
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::BasicResponse() {
+ MOZ_ASSERT(!mWrappedResponse,
+ "Can't BasicResponse a already wrapped response");
+ SafeRefPtr<InternalResponse> basic = CreateIncompleteCopy();
+ basic->mType = ResponseType::Basic;
+ basic->mHeaders = InternalHeaders::BasicHeaders(Headers());
+ basic->mWrappedResponse = SafeRefPtrFromThis();
+ return basic;
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::CORSResponse() {
+ MOZ_ASSERT(!mWrappedResponse,
+ "Can't CORSResponse a already wrapped response");
+ SafeRefPtr<InternalResponse> cors = CreateIncompleteCopy();
+ cors->mType = ResponseType::Cors;
+ cors->mHeaders = InternalHeaders::CORSHeaders(Headers(), mCredentialsMode);
+ cors->mWrappedResponse = SafeRefPtrFromThis();
+ return cors;
+}
+
+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);
+
+ rv = randomGenerator->GenerateRandomBytesInto(randomNumber);
+ 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;
+ }
+
+ 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;
+ }
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::Unfiltered() {
+ SafeRefPtr<InternalResponse> ref = mWrappedResponse.clonePtr();
+ if (!ref) {
+ ref = SafeRefPtrFromThis();
+ }
+ return ref;
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::OpaqueResponse() {
+ MOZ_ASSERT(!mWrappedResponse,
+ "Can't OpaqueResponse a already wrapped response");
+ SafeRefPtr<InternalResponse> response =
+ MakeSafeRefPtr<InternalResponse>(0, ""_ns);
+ response->mType = ResponseType::Opaque;
+ response->mChannelInfo = mChannelInfo;
+ if (mPrincipalInfo) {
+ response->mPrincipalInfo =
+ MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
+ }
+ response->mWrappedResponse = SafeRefPtrFromThis();
+ return response;
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::OpaqueRedirectResponse() {
+ MOZ_ASSERT(!mWrappedResponse,
+ "Can't OpaqueRedirectResponse a already wrapped response");
+ MOZ_ASSERT(!mURLList.IsEmpty(),
+ "URLList should not be emtpy for internalResponse");
+ SafeRefPtr<InternalResponse> response = OpaqueResponse();
+ response->mType = ResponseType::Opaqueredirect;
+ response->mURLList = mURLList.Clone();
+ return response;
+}
+
+SafeRefPtr<InternalResponse> InternalResponse::CreateIncompleteCopy() {
+ SafeRefPtr<InternalResponse> copy =
+ MakeSafeRefPtr<InternalResponse>(mStatus, mStatusText);
+ copy->mType = mType;
+ copy->mURLList = mURLList.Clone();
+ copy->mChannelInfo = mChannelInfo;
+ if (mPrincipalInfo) {
+ copy->mPrincipalInfo =
+ MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
+ }
+ return copy;
+}
+
+ParentToChildInternalResponse ToParentToChild(
+ const ParentToParentInternalResponse& aResponse,
+ NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
+ ParentToChildInternalResponse result(aResponse.metadata(), Nothing(),
+ aResponse.bodySize(), Nothing());
+
+ if (aResponse.body().isSome()) {
+ result.body() = Some(ToParentToChildStream(
+ aResponse.body().ref(), aResponse.bodySize(), aBackgroundParent));
+ }
+ if (aResponse.alternativeBody().isSome()) {
+ result.alternativeBody() = Some(ToParentToChildStream(
+ aResponse.alternativeBody().ref(), InternalResponse::UNKNOWN_BODY_SIZE,
+ aBackgroundParent));
+ }
+
+ return result;
+}
+
+} // namespace mozilla::dom