1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "InternalResponse.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/RefPtr.h"
11 #include "mozilla/dom/FetchStreamUtils.h"
12 #include "mozilla/dom/FetchTypes.h"
13 #include "mozilla/dom/InternalHeaders.h"
14 #include "mozilla/dom/cache/CacheTypes.h"
15 #include "mozilla/ipc/PBackgroundSharedTypes.h"
16 #include "mozilla/ipc/IPCStreamUtils.h"
17 #include "mozilla/RandomNum.h"
18 #include "mozilla/RemoteLazyInputStreamStorage.h"
19 #include "nsIRandomGenerator.h"
20 #include "nsStreamUtils.h"
22 namespace mozilla::dom
{
26 // Const variable for generate padding size
27 // XXX This will be tweaked to something more meaningful in Bug 1383656.
28 const uint32_t kMaxRandomNumber
= 102400;
32 InternalResponse::InternalResponse(uint16_t aStatus
,
33 const nsACString
& aStatusText
,
34 RequestCredentials aCredentialsMode
)
35 : mType(ResponseType::Default
),
37 mStatusText(aStatusText
),
38 mHeaders(new InternalHeaders(HeadersGuardEnum::Response
)),
39 mBodySize(UNKNOWN_BODY_SIZE
),
40 mPaddingSize(UNKNOWN_PADDING_SIZE
),
42 mCredentialsMode(aCredentialsMode
),
45 /* static */ SafeRefPtr
<InternalResponse
> InternalResponse::FromIPC(
46 const ParentToParentInternalResponse
& aIPCResponse
) {
47 MOZ_ASSERT(XRE_IsParentProcess());
48 return FromIPCTemplate(aIPCResponse
);
51 /* static */ SafeRefPtr
<InternalResponse
> InternalResponse::FromIPC(
52 const ParentToChildInternalResponse
& aIPCResponse
) {
53 MOZ_ASSERT(XRE_IsContentProcess());
54 return FromIPCTemplate(aIPCResponse
);
58 /* static */ SafeRefPtr
<InternalResponse
> InternalResponse::FromIPCTemplate(
59 const T
& aIPCResponse
) {
60 if (aIPCResponse
.metadata().type() == ResponseType::Error
) {
61 return InternalResponse::NetworkError(aIPCResponse
.metadata().errorCode());
64 SafeRefPtr
<InternalResponse
> response
= MakeSafeRefPtr
<InternalResponse
>(
65 aIPCResponse
.metadata().status(), aIPCResponse
.metadata().statusText());
67 response
->SetURLList(aIPCResponse
.metadata().urlList());
69 new InternalHeaders(aIPCResponse
.metadata().headers(),
70 aIPCResponse
.metadata().headersGuard());
72 if (aIPCResponse
.body()) {
73 auto bodySize
= aIPCResponse
.bodySize();
74 auto body
= ToInputStream(*aIPCResponse
.body());
75 response
->SetBody(body
.get(), bodySize
);
78 response
->SetAlternativeDataType(
79 aIPCResponse
.metadata().alternativeDataType());
81 if (aIPCResponse
.alternativeBody()) {
82 auto alternativeBody
= ToInputStream(*aIPCResponse
.alternativeBody());
83 response
->SetAlternativeBody(alternativeBody
.get());
86 response
->InitChannelInfo(aIPCResponse
.metadata().securityInfo());
88 if (aIPCResponse
.metadata().principalInfo()) {
89 response
->SetPrincipalInfo(MakeUnique
<mozilla::ipc::PrincipalInfo
>(
90 aIPCResponse
.metadata().principalInfo().ref()));
93 nsAutoCString
bodyBlobURISpec(aIPCResponse
.metadata().bodyBlobURISpec());
94 response
->SetBodyBlobURISpec(bodyBlobURISpec
);
95 nsAutoString
bodyLocalPath(aIPCResponse
.metadata().bodyLocalPath());
96 response
->SetBodyLocalPath(bodyLocalPath
);
98 response
->mCredentialsMode
= aIPCResponse
.metadata().credentialsMode();
100 switch (aIPCResponse
.metadata().type()) {
101 case ResponseType::Basic
:
102 response
= response
->BasicResponse();
104 case ResponseType::Cors
:
105 response
= response
->CORSResponse();
107 case ResponseType::Default
:
109 case ResponseType::Opaque
:
110 response
= response
->OpaqueResponse();
112 case ResponseType::Opaqueredirect
:
113 response
= response
->OpaqueRedirectResponse();
116 MOZ_CRASH("Unexpected ResponseType!");
119 MOZ_ASSERT(response
);
124 InternalResponse::~InternalResponse() = default;
126 InternalResponseMetadata
InternalResponse::GetMetadata() {
127 nsTArray
<HeadersEntry
> headers
;
128 HeadersGuardEnum headersGuard
;
129 UnfilteredHeaders()->ToIPC(headers
, headersGuard
);
131 Maybe
<mozilla::ipc::PrincipalInfo
> principalInfo
=
132 mPrincipalInfo
? Some(*mPrincipalInfo
) : Nothing();
134 nsAutoCString
bodyBlobURISpec(BodyBlobURISpec());
135 nsAutoString
bodyLocalPath(BodyLocalPath());
137 // Note: all the arguments are copied rather than moved, which would be more
138 // efficient, because there's no move-friendly constructor generated.
139 nsCOMPtr
<nsITransportSecurityInfo
> securityInfo(mChannelInfo
.SecurityInfo());
140 return InternalResponseMetadata(
141 mType
, GetUnfilteredURLList(), GetUnfilteredStatus(),
142 GetUnfilteredStatusText(), headersGuard
, headers
, mErrorCode
,
143 GetAlternativeDataType(), securityInfo
, principalInfo
, bodyBlobURISpec
,
144 bodyLocalPath
, GetCredentialsMode());
147 void InternalResponse::ToChildToParentInternalResponse(
148 ChildToParentInternalResponse
* aIPCResponse
,
149 mozilla::ipc::PBackgroundChild
* aManager
) {
150 *aIPCResponse
= ChildToParentInternalResponse(GetMetadata(), Nothing(),
151 UNKNOWN_BODY_SIZE
, Nothing());
153 nsCOMPtr
<nsIInputStream
> body
;
155 GetUnfilteredBody(getter_AddRefs(body
), &bodySize
);
158 aIPCResponse
->body().emplace(ChildToParentStream());
159 aIPCResponse
->bodySize() = bodySize
;
161 DebugOnly
<bool> ok
= mozilla::ipc::SerializeIPCStream(
162 body
.forget(), aIPCResponse
->body()->stream(), /* aAllowLazy */ false);
166 nsCOMPtr
<nsIInputStream
> alternativeBody
= TakeAlternativeBody();
167 if (alternativeBody
) {
168 aIPCResponse
->alternativeBody().emplace(ChildToParentStream());
170 DebugOnly
<bool> ok
= mozilla::ipc::SerializeIPCStream(
171 alternativeBody
.forget(), aIPCResponse
->alternativeBody()->stream(),
172 /* aAllowLazy */ false);
177 ParentToParentInternalResponse
178 InternalResponse::ToParentToParentInternalResponse() {
179 ParentToParentInternalResponse
result(GetMetadata(), Nothing(),
180 UNKNOWN_BODY_SIZE
, Nothing());
182 nsCOMPtr
<nsIInputStream
> body
;
184 GetUnfilteredBody(getter_AddRefs(body
), &bodySize
);
187 result
.body() = Some(ToParentToParentStream(WrapNotNull(body
), bodySize
));
188 result
.bodySize() = bodySize
;
191 nsCOMPtr
<nsIInputStream
> alternativeBody
= TakeAlternativeBody();
192 if (alternativeBody
) {
193 result
.alternativeBody() = Some(ToParentToParentStream(
194 WrapNotNull(alternativeBody
), UNKNOWN_BODY_SIZE
));
200 ParentToChildInternalResponse
InternalResponse::ToParentToChildInternalResponse(
201 NotNull
<mozilla::ipc::PBackgroundParent
*> aBackgroundParent
) {
202 ParentToChildInternalResponse
result(GetMetadata(), Nothing(),
203 UNKNOWN_BODY_SIZE
, Nothing());
205 nsCOMPtr
<nsIInputStream
> body
;
207 GetUnfilteredBody(getter_AddRefs(body
), &bodySize
);
210 ParentToChildStream bodyStream
= ToParentToChildStream(
211 WrapNotNull(body
), bodySize
, aBackgroundParent
, mSerializeAsLazy
);
212 // The body stream can fail to serialize as an IPCStream. In the case, the
213 // IPCStream's type would be T__None. Don't set up IPCInternalResponse's
214 // body with the failed IPCStream.
215 if (mSerializeAsLazy
|| bodyStream
.get_IPCStream().stream().type() !=
216 mozilla::ipc::InputStreamParams::T__None
) {
217 result
.body() = Some(bodyStream
);
218 result
.bodySize() = bodySize
;
222 nsCOMPtr
<nsIInputStream
> alternativeBody
= TakeAlternativeBody();
223 if (alternativeBody
) {
224 ParentToChildStream alterBodyStream
=
225 ToParentToChildStream(WrapNotNull(alternativeBody
), UNKNOWN_BODY_SIZE
,
226 aBackgroundParent
, mSerializeAsLazy
);
227 // The body stream can fail to serialize as an IPCStream. In the case, the
228 // IPCStream's type would be T__None. Don't set up IPCInternalResponse's
229 // body with the failed IPCStream.
230 if (mSerializeAsLazy
|| alterBodyStream
.get_IPCStream().stream().type() !=
231 mozilla::ipc::InputStreamParams::T__None
) {
232 result
.alternativeBody() = Some(alterBodyStream
);
239 SafeRefPtr
<InternalResponse
> InternalResponse::Clone(CloneType aCloneType
) {
240 SafeRefPtr
<InternalResponse
> clone
= CreateIncompleteCopy();
241 clone
->mCloned
= (mCloned
= true);
243 clone
->mHeaders
= new InternalHeaders(*mHeaders
);
245 // Make sure the clone response will have the same padding size.
246 clone
->mPaddingInfo
= mPaddingInfo
;
247 clone
->mPaddingSize
= mPaddingSize
;
249 clone
->mCacheInfoChannel
= mCacheInfoChannel
;
250 clone
->mCredentialsMode
= mCredentialsMode
;
252 if (mWrappedResponse
) {
253 clone
->mWrappedResponse
= mWrappedResponse
->Clone(aCloneType
);
258 if (!mBody
|| aCloneType
== eDontCloneInputStream
) {
262 nsCOMPtr
<nsIInputStream
> clonedBody
;
263 nsCOMPtr
<nsIInputStream
> replacementBody
;
265 nsresult rv
= NS_CloneInputStream(mBody
, getter_AddRefs(clonedBody
),
266 getter_AddRefs(replacementBody
));
267 if (NS_WARN_IF(NS_FAILED(rv
))) {
271 clone
->mBody
.swap(clonedBody
);
272 if (replacementBody
) {
273 mBody
.swap(replacementBody
);
279 SafeRefPtr
<InternalResponse
> InternalResponse::BasicResponse() {
280 MOZ_ASSERT(!mWrappedResponse
,
281 "Can't BasicResponse a already wrapped response");
282 SafeRefPtr
<InternalResponse
> basic
= CreateIncompleteCopy();
283 basic
->mType
= ResponseType::Basic
;
284 basic
->mHeaders
= InternalHeaders::BasicHeaders(Headers());
285 basic
->mWrappedResponse
= SafeRefPtrFromThis();
289 SafeRefPtr
<InternalResponse
> InternalResponse::CORSResponse() {
290 MOZ_ASSERT(!mWrappedResponse
,
291 "Can't CORSResponse a already wrapped response");
292 SafeRefPtr
<InternalResponse
> cors
= CreateIncompleteCopy();
293 cors
->mType
= ResponseType::Cors
;
294 cors
->mHeaders
= InternalHeaders::CORSHeaders(Headers(), mCredentialsMode
);
295 cors
->mWrappedResponse
= SafeRefPtrFromThis();
299 uint32_t InternalResponse::GetPaddingInfo() {
300 // If it's an opaque response, the paddingInfo should be generated only when
301 // paddingSize is unknown size.
302 // If it's not, the paddingInfo should be nothing and the paddingSize should
304 MOZ_DIAGNOSTIC_ASSERT(
305 (mType
== ResponseType::Opaque
&& mPaddingSize
== UNKNOWN_PADDING_SIZE
&&
306 mPaddingInfo
.isSome()) ||
307 (mType
== ResponseType::Opaque
&& mPaddingSize
!= UNKNOWN_PADDING_SIZE
&&
308 mPaddingInfo
.isNothing()) ||
309 (mType
!= ResponseType::Opaque
&& mPaddingSize
== UNKNOWN_PADDING_SIZE
&&
310 mPaddingInfo
.isNothing()));
311 return mPaddingInfo
.isSome() ? mPaddingInfo
.ref() : 0;
314 nsresult
InternalResponse::GeneratePaddingInfo() {
315 MOZ_DIAGNOSTIC_ASSERT(mType
== ResponseType::Opaque
);
316 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize
== UNKNOWN_PADDING_SIZE
);
318 // Utilize random generator to generator a random number
320 uint32_t randomNumber
= 0;
321 nsCOMPtr
<nsIRandomGenerator
> randomGenerator
=
322 do_GetService("@mozilla.org/security/random-generator;1", &rv
);
323 if (NS_WARN_IF(NS_FAILED(rv
))) {
324 Maybe
<uint64_t> maybeRandomNum
= RandomUint64();
325 if (maybeRandomNum
.isSome()) {
326 mPaddingInfo
.emplace(uint32_t(maybeRandomNum
.value() % kMaxRandomNumber
));
332 MOZ_DIAGNOSTIC_ASSERT(randomGenerator
);
335 rv
= randomGenerator
->GenerateRandomBytes(sizeof(randomNumber
), &buffer
);
336 if (NS_WARN_IF(NS_FAILED(rv
))) {
337 Maybe
<uint64_t> maybeRandomNum
= RandomUint64();
338 if (maybeRandomNum
.isSome()) {
339 mPaddingInfo
.emplace(uint32_t(maybeRandomNum
.value() % kMaxRandomNumber
));
345 memcpy(&randomNumber
, buffer
, sizeof(randomNumber
));
348 mPaddingInfo
.emplace(randomNumber
% kMaxRandomNumber
);
353 int64_t InternalResponse::GetPaddingSize() {
354 // We initialize padding size to an unknown size (-1). After cached, we only
355 // pad opaque response. Opaque response's padding size might be unknown before
357 MOZ_DIAGNOSTIC_ASSERT(mType
== ResponseType::Opaque
||
358 mPaddingSize
== UNKNOWN_PADDING_SIZE
);
359 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize
== UNKNOWN_PADDING_SIZE
||
365 void InternalResponse::SetPaddingSize(int64_t aPaddingSize
) {
366 // We should only pad the opaque response.
367 MOZ_DIAGNOSTIC_ASSERT(
368 (mType
== ResponseType::Opaque
) !=
369 (aPaddingSize
== InternalResponse::UNKNOWN_PADDING_SIZE
));
370 MOZ_DIAGNOSTIC_ASSERT(aPaddingSize
== UNKNOWN_PADDING_SIZE
||
373 mPaddingSize
= aPaddingSize
;
376 void InternalResponse::SetPrincipalInfo(
377 UniquePtr
<mozilla::ipc::PrincipalInfo
> aPrincipalInfo
) {
378 mPrincipalInfo
= std::move(aPrincipalInfo
);
381 LoadTainting
InternalResponse::GetTainting() const {
383 case ResponseType::Cors
:
384 return LoadTainting::CORS
;
385 case ResponseType::Opaque
:
386 return LoadTainting::Opaque
;
388 return LoadTainting::Basic
;
392 SafeRefPtr
<InternalResponse
> InternalResponse::Unfiltered() {
393 SafeRefPtr
<InternalResponse
> ref
= mWrappedResponse
.clonePtr();
395 ref
= SafeRefPtrFromThis();
400 SafeRefPtr
<InternalResponse
> InternalResponse::OpaqueResponse() {
401 MOZ_ASSERT(!mWrappedResponse
,
402 "Can't OpaqueResponse a already wrapped response");
403 SafeRefPtr
<InternalResponse
> response
=
404 MakeSafeRefPtr
<InternalResponse
>(0, ""_ns
);
405 response
->mType
= ResponseType::Opaque
;
406 response
->mChannelInfo
= mChannelInfo
;
407 if (mPrincipalInfo
) {
408 response
->mPrincipalInfo
=
409 MakeUnique
<mozilla::ipc::PrincipalInfo
>(*mPrincipalInfo
);
411 response
->mWrappedResponse
= SafeRefPtrFromThis();
415 SafeRefPtr
<InternalResponse
> InternalResponse::OpaqueRedirectResponse() {
416 MOZ_ASSERT(!mWrappedResponse
,
417 "Can't OpaqueRedirectResponse a already wrapped response");
418 MOZ_ASSERT(!mURLList
.IsEmpty(),
419 "URLList should not be emtpy for internalResponse");
420 SafeRefPtr
<InternalResponse
> response
= OpaqueResponse();
421 response
->mType
= ResponseType::Opaqueredirect
;
422 response
->mURLList
= mURLList
.Clone();
426 SafeRefPtr
<InternalResponse
> InternalResponse::CreateIncompleteCopy() {
427 SafeRefPtr
<InternalResponse
> copy
=
428 MakeSafeRefPtr
<InternalResponse
>(mStatus
, mStatusText
);
430 copy
->mURLList
= mURLList
.Clone();
431 copy
->mChannelInfo
= mChannelInfo
;
432 if (mPrincipalInfo
) {
433 copy
->mPrincipalInfo
=
434 MakeUnique
<mozilla::ipc::PrincipalInfo
>(*mPrincipalInfo
);
439 ParentToChildInternalResponse
ToParentToChild(
440 const ParentToParentInternalResponse
& aResponse
,
441 NotNull
<mozilla::ipc::PBackgroundParent
*> aBackgroundParent
) {
442 ParentToChildInternalResponse
result(aResponse
.metadata(), Nothing(),
443 aResponse
.bodySize(), Nothing());
445 if (aResponse
.body().isSome()) {
446 result
.body() = Some(ToParentToChildStream(
447 aResponse
.body().ref(), aResponse
.bodySize(), aBackgroundParent
));
449 if (aResponse
.alternativeBody().isSome()) {
450 result
.alternativeBody() = Some(ToParentToChildStream(
451 aResponse
.alternativeBody().ref(), InternalResponse::UNKNOWN_BODY_SIZE
,
458 } // namespace mozilla::dom