Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / dom / fetch / InternalResponse.cpp
blob1603d6e4cb732a83367d58177ce84f82e43746d7
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 {
24 namespace {
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;
30 } // namespace
32 InternalResponse::InternalResponse(uint16_t aStatus,
33 const nsACString& aStatusText,
34 RequestCredentials aCredentialsMode)
35 : mType(ResponseType::Default),
36 mStatus(aStatus),
37 mStatusText(aStatusText),
38 mHeaders(new InternalHeaders(HeadersGuardEnum::Response)),
39 mBodySize(UNKNOWN_BODY_SIZE),
40 mPaddingSize(UNKNOWN_PADDING_SIZE),
41 mErrorCode(NS_OK),
42 mCredentialsMode(aCredentialsMode),
43 mCloned(false) {}
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);
57 template <typename T>
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());
68 response->mHeaders =
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();
103 break;
104 case ResponseType::Cors:
105 response = response->CORSResponse();
106 break;
107 case ResponseType::Default:
108 break;
109 case ResponseType::Opaque:
110 response = response->OpaqueResponse();
111 break;
112 case ResponseType::Opaqueredirect:
113 response = response->OpaqueRedirectResponse();
114 break;
115 default:
116 MOZ_CRASH("Unexpected ResponseType!");
119 MOZ_ASSERT(response);
121 return 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;
154 int64_t bodySize;
155 GetUnfilteredBody(getter_AddRefs(body), &bodySize);
157 if (body) {
158 aIPCResponse->body().emplace(ChildToParentStream());
159 aIPCResponse->bodySize() = bodySize;
161 DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream(
162 body.forget(), aIPCResponse->body()->stream(), /* aAllowLazy */ false);
163 MOZ_ASSERT(ok);
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);
173 MOZ_ASSERT(ok);
177 ParentToParentInternalResponse
178 InternalResponse::ToParentToParentInternalResponse() {
179 ParentToParentInternalResponse result(GetMetadata(), Nothing(),
180 UNKNOWN_BODY_SIZE, Nothing());
182 nsCOMPtr<nsIInputStream> body;
183 int64_t bodySize;
184 GetUnfilteredBody(getter_AddRefs(body), &bodySize);
186 if (body) {
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));
197 return result;
200 ParentToChildInternalResponse InternalResponse::ToParentToChildInternalResponse(
201 NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
202 ParentToChildInternalResponse result(GetMetadata(), Nothing(),
203 UNKNOWN_BODY_SIZE, Nothing());
205 nsCOMPtr<nsIInputStream> body;
206 int64_t bodySize;
207 GetUnfilteredBody(getter_AddRefs(body), &bodySize);
209 if (body) {
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);
236 return result;
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);
254 MOZ_ASSERT(!mBody);
255 return clone;
258 if (!mBody || aCloneType == eDontCloneInputStream) {
259 return clone;
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))) {
268 return nullptr;
271 clone->mBody.swap(clonedBody);
272 if (replacementBody) {
273 mBody.swap(replacementBody);
276 return clone;
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();
286 return basic;
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();
296 return cors;
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
303 // be unknown size.
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
319 nsresult rv;
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));
327 return NS_OK;
329 return rv;
332 MOZ_DIAGNOSTIC_ASSERT(randomGenerator);
334 rv = randomGenerator->GenerateRandomBytesInto(randomNumber);
335 if (NS_WARN_IF(NS_FAILED(rv))) {
336 Maybe<uint64_t> maybeRandomNum = RandomUint64();
337 if (maybeRandomNum.isSome()) {
338 mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber));
339 return NS_OK;
341 return rv;
344 mPaddingInfo.emplace(randomNumber % kMaxRandomNumber);
346 return rv;
349 int64_t InternalResponse::GetPaddingSize() {
350 // We initialize padding size to an unknown size (-1). After cached, we only
351 // pad opaque response. Opaque response's padding size might be unknown before
352 // cached.
353 MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque ||
354 mPaddingSize == UNKNOWN_PADDING_SIZE);
355 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE ||
356 mPaddingSize >= 0);
358 return mPaddingSize;
361 void InternalResponse::SetPaddingSize(int64_t aPaddingSize) {
362 // We should only pad the opaque response.
363 MOZ_DIAGNOSTIC_ASSERT(
364 (mType == ResponseType::Opaque) !=
365 (aPaddingSize == InternalResponse::UNKNOWN_PADDING_SIZE));
366 MOZ_DIAGNOSTIC_ASSERT(aPaddingSize == UNKNOWN_PADDING_SIZE ||
367 aPaddingSize >= 0);
369 mPaddingSize = aPaddingSize;
372 void InternalResponse::SetPrincipalInfo(
373 UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo) {
374 mPrincipalInfo = std::move(aPrincipalInfo);
377 LoadTainting InternalResponse::GetTainting() const {
378 switch (mType) {
379 case ResponseType::Cors:
380 return LoadTainting::CORS;
381 case ResponseType::Opaque:
382 return LoadTainting::Opaque;
383 default:
384 return LoadTainting::Basic;
388 SafeRefPtr<InternalResponse> InternalResponse::Unfiltered() {
389 SafeRefPtr<InternalResponse> ref = mWrappedResponse.clonePtr();
390 if (!ref) {
391 ref = SafeRefPtrFromThis();
393 return ref;
396 SafeRefPtr<InternalResponse> InternalResponse::OpaqueResponse() {
397 MOZ_ASSERT(!mWrappedResponse,
398 "Can't OpaqueResponse a already wrapped response");
399 SafeRefPtr<InternalResponse> response =
400 MakeSafeRefPtr<InternalResponse>(0, ""_ns);
401 response->mType = ResponseType::Opaque;
402 response->mChannelInfo = mChannelInfo;
403 if (mPrincipalInfo) {
404 response->mPrincipalInfo =
405 MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
407 response->mWrappedResponse = SafeRefPtrFromThis();
408 return response;
411 SafeRefPtr<InternalResponse> InternalResponse::OpaqueRedirectResponse() {
412 MOZ_ASSERT(!mWrappedResponse,
413 "Can't OpaqueRedirectResponse a already wrapped response");
414 MOZ_ASSERT(!mURLList.IsEmpty(),
415 "URLList should not be emtpy for internalResponse");
416 SafeRefPtr<InternalResponse> response = OpaqueResponse();
417 response->mType = ResponseType::Opaqueredirect;
418 response->mURLList = mURLList.Clone();
419 return response;
422 SafeRefPtr<InternalResponse> InternalResponse::CreateIncompleteCopy() {
423 SafeRefPtr<InternalResponse> copy =
424 MakeSafeRefPtr<InternalResponse>(mStatus, mStatusText);
425 copy->mType = mType;
426 copy->mURLList = mURLList.Clone();
427 copy->mChannelInfo = mChannelInfo;
428 if (mPrincipalInfo) {
429 copy->mPrincipalInfo =
430 MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
432 return copy;
435 ParentToChildInternalResponse ToParentToChild(
436 const ParentToParentInternalResponse& aResponse,
437 NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
438 ParentToChildInternalResponse result(aResponse.metadata(), Nothing(),
439 aResponse.bodySize(), Nothing());
441 if (aResponse.body().isSome()) {
442 result.body() = Some(ToParentToChildStream(
443 aResponse.body().ref(), aResponse.bodySize(), aBackgroundParent));
445 if (aResponse.alternativeBody().isSome()) {
446 result.alternativeBody() = Some(ToParentToChildStream(
447 aResponse.alternativeBody().ref(), InternalResponse::UNKNOWN_BODY_SIZE,
448 aBackgroundParent));
451 return result;
454 } // namespace mozilla::dom