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/FetchTypes.h"
12 #include "mozilla/dom/InternalHeaders.h"
13 #include "mozilla/dom/cache/CacheTypes.h"
14 #include "mozilla/ipc/PBackgroundSharedTypes.h"
15 #include "mozilla/ipc/IPCStreamUtils.h"
16 #include "mozilla/RandomNum.h"
17 #include "mozilla/RemoteLazyInputStreamStorage.h"
18 #include "nsIRandomGenerator.h"
19 #include "nsStreamUtils.h"
21 namespace mozilla::dom
{
25 // Const variable for generate padding size
26 // XXX This will be tweaked to something more meaningful in Bug 1383656.
27 const uint32_t kMaxRandomNumber
= 102400;
29 nsCOMPtr
<nsIInputStream
> TakeStreamFromStorage(
30 const BodyStreamVariant
& aVariant
, int64_t aBodySize
) {
31 MOZ_ASSERT(aVariant
.type() == BodyStreamVariant::TParentToParentStream
);
32 const auto& uuid
= aVariant
.get_ParentToParentStream().uuid();
34 auto storageOrErr
= RemoteLazyInputStreamStorage::Get();
35 MOZ_ASSERT(storageOrErr
.isOk());
36 auto storage
= storageOrErr
.unwrap();
37 auto stream
= storage
->ForgetStream(uuid
);
45 InternalResponse::InternalResponse(uint16_t aStatus
,
46 const nsACString
& aStatusText
,
47 RequestCredentials aCredentialsMode
)
48 : mType(ResponseType::Default
),
50 mStatusText(aStatusText
),
51 mHeaders(new InternalHeaders(HeadersGuardEnum::Response
)),
52 mBodySize(UNKNOWN_BODY_SIZE
),
53 mPaddingSize(UNKNOWN_PADDING_SIZE
),
55 mCredentialsMode(aCredentialsMode
) {}
57 /* static */ RefPtr
<InternalResponse
> InternalResponse::FromIPC(
58 const IPCInternalResponse
& aIPCResponse
) {
59 if (aIPCResponse
.type() == ResponseType::Error
) {
60 return InternalResponse::NetworkError(aIPCResponse
.errorCode());
63 RefPtr
<InternalResponse
> response
=
64 new InternalResponse(aIPCResponse
.status(), aIPCResponse
.statusText());
66 response
->SetURLList(aIPCResponse
.urlList());
68 new InternalHeaders(aIPCResponse
.headers(), aIPCResponse
.headersGuard());
70 if (aIPCResponse
.body()) {
71 auto bodySize
= aIPCResponse
.bodySize();
72 nsCOMPtr
<nsIInputStream
> body
=
73 TakeStreamFromStorage(*aIPCResponse
.body(), bodySize
);
74 response
->SetBody(body
, bodySize
);
77 response
->SetAlternativeDataType(aIPCResponse
.alternativeDataType());
79 if (aIPCResponse
.alternativeBody()) {
80 nsCOMPtr
<nsIInputStream
> alternativeBody
= TakeStreamFromStorage(
81 *aIPCResponse
.alternativeBody(), UNKNOWN_BODY_SIZE
);
82 response
->SetAlternativeBody(alternativeBody
);
85 response
->InitChannelInfo(aIPCResponse
.channelInfo());
87 if (aIPCResponse
.principalInfo()) {
88 response
->SetPrincipalInfo(MakeUnique
<mozilla::ipc::PrincipalInfo
>(
89 aIPCResponse
.principalInfo().ref()));
92 switch (aIPCResponse
.type()) {
93 case ResponseType::Basic
:
94 response
= response
->BasicResponse();
96 case ResponseType::Cors
:
97 response
= response
->CORSResponse();
99 case ResponseType::Default
:
101 case ResponseType::Opaque
:
102 response
= response
->OpaqueResponse();
104 case ResponseType::Opaqueredirect
:
105 response
= response
->OpaqueRedirectResponse();
108 MOZ_CRASH("Unexpected ResponseType!");
111 MOZ_ASSERT(response
);
116 InternalResponse::~InternalResponse() = default;
118 void InternalResponse::ToIPC(
119 IPCInternalResponse
* aIPCResponse
, mozilla::ipc::PBackgroundChild
* aManager
,
120 UniquePtr
<mozilla::ipc::AutoIPCStream
>& aAutoBodyStream
,
121 UniquePtr
<mozilla::ipc::AutoIPCStream
>& aAutoAlternativeBodyStream
) {
122 nsTArray
<HeadersEntry
> headers
;
123 HeadersGuardEnum headersGuard
;
124 UnfilteredHeaders()->ToIPC(headers
, headersGuard
);
126 Maybe
<mozilla::ipc::PrincipalInfo
> principalInfo
=
127 mPrincipalInfo
? Some(*mPrincipalInfo
) : Nothing();
129 // Note: all the arguments are copied rather than moved, which would be more
130 // efficient, because there's no move-friendly constructor generated.
132 IPCInternalResponse(mType
, GetUnfilteredURLList(), GetUnfilteredStatus(),
133 GetUnfilteredStatusText(), headersGuard
, headers
,
134 Nothing(), static_cast<uint64_t>(UNKNOWN_BODY_SIZE
),
135 mErrorCode
, GetAlternativeDataType(), Nothing(),
136 mChannelInfo
.AsIPCChannelInfo(), principalInfo
);
138 nsCOMPtr
<nsIInputStream
> body
;
140 GetUnfilteredBody(getter_AddRefs(body
), &bodySize
);
143 aIPCResponse
->body().emplace(ChildToParentStream());
144 aIPCResponse
->bodySize() = bodySize
;
146 aAutoBodyStream
.reset(new mozilla::ipc::AutoIPCStream(
147 aIPCResponse
->body()->get_ChildToParentStream().stream()));
148 DebugOnly
<bool> ok
= aAutoBodyStream
->Serialize(body
, aManager
);
152 nsCOMPtr
<nsIInputStream
> alternativeBody
= TakeAlternativeBody();
153 if (alternativeBody
) {
154 aIPCResponse
->alternativeBody().emplace(ChildToParentStream());
156 aAutoAlternativeBodyStream
.reset(new mozilla::ipc::AutoIPCStream(
157 aIPCResponse
->alternativeBody()->get_ChildToParentStream().stream()));
159 aAutoAlternativeBodyStream
->Serialize(alternativeBody
, aManager
);
164 already_AddRefed
<InternalResponse
> InternalResponse::Clone(
165 CloneType aCloneType
) {
166 RefPtr
<InternalResponse
> clone
= CreateIncompleteCopy();
168 clone
->mHeaders
= new InternalHeaders(*mHeaders
);
170 // Make sure the clone response will have the same padding size.
171 clone
->mPaddingInfo
= mPaddingInfo
;
172 clone
->mPaddingSize
= mPaddingSize
;
174 clone
->mCacheInfoChannel
= mCacheInfoChannel
;
176 if (mWrappedResponse
) {
177 clone
->mWrappedResponse
= mWrappedResponse
->Clone(aCloneType
);
179 return clone
.forget();
182 if (!mBody
|| aCloneType
== eDontCloneInputStream
) {
183 return clone
.forget();
186 nsCOMPtr
<nsIInputStream
> clonedBody
;
187 nsCOMPtr
<nsIInputStream
> replacementBody
;
189 nsresult rv
= NS_CloneInputStream(mBody
, getter_AddRefs(clonedBody
),
190 getter_AddRefs(replacementBody
));
191 if (NS_WARN_IF(NS_FAILED(rv
))) {
195 clone
->mBody
.swap(clonedBody
);
196 if (replacementBody
) {
197 mBody
.swap(replacementBody
);
200 return clone
.forget();
203 already_AddRefed
<InternalResponse
> InternalResponse::BasicResponse() {
204 MOZ_ASSERT(!mWrappedResponse
,
205 "Can't BasicResponse a already wrapped response");
206 RefPtr
<InternalResponse
> basic
= CreateIncompleteCopy();
207 basic
->mType
= ResponseType::Basic
;
208 basic
->mHeaders
= InternalHeaders::BasicHeaders(Headers());
209 basic
->mWrappedResponse
= this;
210 return basic
.forget();
213 already_AddRefed
<InternalResponse
> InternalResponse::CORSResponse() {
214 MOZ_ASSERT(!mWrappedResponse
,
215 "Can't CORSResponse a already wrapped response");
216 RefPtr
<InternalResponse
> cors
= CreateIncompleteCopy();
217 cors
->mType
= ResponseType::Cors
;
218 cors
->mHeaders
= InternalHeaders::CORSHeaders(Headers(), mCredentialsMode
);
219 cors
->mWrappedResponse
= this;
220 return cors
.forget();
223 uint32_t InternalResponse::GetPaddingInfo() {
224 // If it's an opaque response, the paddingInfo should be generated only when
225 // paddingSize is unknown size.
226 // If it's not, the paddingInfo should be nothing and the paddingSize should
228 MOZ_DIAGNOSTIC_ASSERT(
229 (mType
== ResponseType::Opaque
&& mPaddingSize
== UNKNOWN_PADDING_SIZE
&&
230 mPaddingInfo
.isSome()) ||
231 (mType
== ResponseType::Opaque
&& mPaddingSize
!= UNKNOWN_PADDING_SIZE
&&
232 mPaddingInfo
.isNothing()) ||
233 (mType
!= ResponseType::Opaque
&& mPaddingSize
== UNKNOWN_PADDING_SIZE
&&
234 mPaddingInfo
.isNothing()));
235 return mPaddingInfo
.isSome() ? mPaddingInfo
.ref() : 0;
238 nsresult
InternalResponse::GeneratePaddingInfo() {
239 MOZ_DIAGNOSTIC_ASSERT(mType
== ResponseType::Opaque
);
240 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize
== UNKNOWN_PADDING_SIZE
);
242 // Utilize random generator to generator a random number
244 uint32_t randomNumber
= 0;
245 nsCOMPtr
<nsIRandomGenerator
> randomGenerator
=
246 do_GetService("@mozilla.org/security/random-generator;1", &rv
);
247 if (NS_WARN_IF(NS_FAILED(rv
))) {
248 Maybe
<uint64_t> maybeRandomNum
= RandomUint64();
249 if (maybeRandomNum
.isSome()) {
250 mPaddingInfo
.emplace(uint32_t(maybeRandomNum
.value() % kMaxRandomNumber
));
256 MOZ_DIAGNOSTIC_ASSERT(randomGenerator
);
259 rv
= randomGenerator
->GenerateRandomBytes(sizeof(randomNumber
), &buffer
);
260 if (NS_WARN_IF(NS_FAILED(rv
))) {
261 Maybe
<uint64_t> maybeRandomNum
= RandomUint64();
262 if (maybeRandomNum
.isSome()) {
263 mPaddingInfo
.emplace(uint32_t(maybeRandomNum
.value() % kMaxRandomNumber
));
269 memcpy(&randomNumber
, buffer
, sizeof(randomNumber
));
272 mPaddingInfo
.emplace(randomNumber
% kMaxRandomNumber
);
277 int64_t InternalResponse::GetPaddingSize() {
278 // We initialize padding size to an unknown size (-1). After cached, we only
279 // pad opaque response. Opaque response's padding size might be unknown before
281 MOZ_DIAGNOSTIC_ASSERT(mType
== ResponseType::Opaque
||
282 mPaddingSize
== UNKNOWN_PADDING_SIZE
);
283 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize
== UNKNOWN_PADDING_SIZE
||
289 void InternalResponse::SetPaddingSize(int64_t aPaddingSize
) {
290 // We should only pad the opaque response.
291 MOZ_DIAGNOSTIC_ASSERT(
292 (mType
== ResponseType::Opaque
) !=
293 (aPaddingSize
== InternalResponse::UNKNOWN_PADDING_SIZE
));
294 MOZ_DIAGNOSTIC_ASSERT(aPaddingSize
== UNKNOWN_PADDING_SIZE
||
297 mPaddingSize
= aPaddingSize
;
300 void InternalResponse::SetPrincipalInfo(
301 UniquePtr
<mozilla::ipc::PrincipalInfo
> aPrincipalInfo
) {
302 mPrincipalInfo
= std::move(aPrincipalInfo
);
305 LoadTainting
InternalResponse::GetTainting() const {
307 case ResponseType::Cors
:
308 return LoadTainting::CORS
;
309 case ResponseType::Opaque
:
310 return LoadTainting::Opaque
;
312 return LoadTainting::Basic
;
316 already_AddRefed
<InternalResponse
> InternalResponse::Unfiltered() {
317 RefPtr
<InternalResponse
> ref
= mWrappedResponse
;
324 already_AddRefed
<InternalResponse
> InternalResponse::OpaqueResponse() {
325 MOZ_ASSERT(!mWrappedResponse
,
326 "Can't OpaqueResponse a already wrapped response");
327 RefPtr
<InternalResponse
> response
= new InternalResponse(0, ""_ns
);
328 response
->mType
= ResponseType::Opaque
;
329 response
->mChannelInfo
= mChannelInfo
;
330 if (mPrincipalInfo
) {
331 response
->mPrincipalInfo
=
332 MakeUnique
<mozilla::ipc::PrincipalInfo
>(*mPrincipalInfo
);
334 response
->mWrappedResponse
= this;
335 return response
.forget();
338 already_AddRefed
<InternalResponse
> InternalResponse::OpaqueRedirectResponse() {
339 MOZ_ASSERT(!mWrappedResponse
,
340 "Can't OpaqueRedirectResponse a already wrapped response");
341 MOZ_ASSERT(!mURLList
.IsEmpty(),
342 "URLList should not be emtpy for internalResponse");
343 RefPtr
<InternalResponse
> response
= OpaqueResponse();
344 response
->mType
= ResponseType::Opaqueredirect
;
345 response
->mURLList
= mURLList
.Clone();
346 return response
.forget();
349 already_AddRefed
<InternalResponse
> InternalResponse::CreateIncompleteCopy() {
350 RefPtr
<InternalResponse
> copy
= new InternalResponse(mStatus
, mStatusText
);
352 copy
->mURLList
= mURLList
.Clone();
353 copy
->mChannelInfo
= mChannelInfo
;
354 if (mPrincipalInfo
) {
355 copy
->mPrincipalInfo
=
356 MakeUnique
<mozilla::ipc::PrincipalInfo
>(*mPrincipalInfo
);
358 return copy
.forget();
361 } // namespace mozilla::dom