InternalResponse.cpp (15614B)
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/. */ 6 7 #include "InternalResponse.h" 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/RandomNum.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/RemoteLazyInputStreamStorage.h" 13 #include "mozilla/dom/FetchStreamUtils.h" 14 #include "mozilla/dom/FetchTypes.h" 15 #include "mozilla/dom/InternalHeaders.h" 16 #include "mozilla/dom/cache/CacheTypes.h" 17 #include "mozilla/ipc/IPCStreamUtils.h" 18 #include "mozilla/ipc/PBackgroundSharedTypes.h" 19 #include "nsIRandomGenerator.h" 20 #include "nsStreamUtils.h" 21 22 namespace mozilla::dom { 23 24 namespace { 25 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; 29 30 } // namespace 31 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) {} 44 45 /* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPC( 46 const ParentToParentInternalResponse& aIPCResponse) { 47 MOZ_ASSERT(XRE_IsParentProcess()); 48 return FromIPCTemplate(aIPCResponse); 49 } 50 51 /* static */ SafeRefPtr<InternalResponse> InternalResponse::FromIPC( 52 const ParentToChildInternalResponse& aIPCResponse) { 53 MOZ_ASSERT(XRE_IsContentProcess()); 54 return FromIPCTemplate(aIPCResponse); 55 } 56 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()); 62 } 63 64 SafeRefPtr<InternalResponse> response = MakeSafeRefPtr<InternalResponse>( 65 aIPCResponse.metadata().status(), aIPCResponse.metadata().statusText()); 66 67 response->SetURLList(aIPCResponse.metadata().urlList()); 68 response->mHeaders = 69 new InternalHeaders(aIPCResponse.metadata().headers(), 70 aIPCResponse.metadata().headersGuard()); 71 72 if (aIPCResponse.body()) { 73 auto bodySize = aIPCResponse.bodySize(); 74 auto body = ToInputStream(*aIPCResponse.body()); 75 response->SetBody(body.get(), bodySize); 76 } 77 78 response->SetAlternativeDataType( 79 aIPCResponse.metadata().alternativeDataType()); 80 81 if (aIPCResponse.alternativeBody()) { 82 auto alternativeBody = ToInputStream(*aIPCResponse.alternativeBody()); 83 response->SetAlternativeBody(alternativeBody.get()); 84 } 85 86 response->InitChannelInfo(aIPCResponse.metadata().securityInfo()); 87 88 if (aIPCResponse.metadata().principalInfo()) { 89 response->SetPrincipalInfo(MakeUnique<mozilla::ipc::PrincipalInfo>( 90 aIPCResponse.metadata().principalInfo().ref())); 91 } 92 93 nsAutoCString bodyBlobURISpec(aIPCResponse.metadata().bodyBlobURISpec()); 94 response->SetBodyBlobURISpec(bodyBlobURISpec); 95 nsAutoString bodyLocalPath(aIPCResponse.metadata().bodyLocalPath()); 96 response->SetBodyLocalPath(bodyLocalPath); 97 98 response->mCredentialsMode = aIPCResponse.metadata().credentialsMode(); 99 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!"); 117 } 118 119 MOZ_ASSERT(response); 120 121 return response; 122 } 123 124 InternalResponse::~InternalResponse() = default; 125 126 InternalResponseMetadata InternalResponse::GetMetadata() { 127 nsTArray<HeadersEntry> headers; 128 HeadersGuardEnum headersGuard; 129 UnfilteredHeaders()->ToIPC(headers, headersGuard); 130 131 Maybe<mozilla::ipc::PrincipalInfo> principalInfo = 132 mPrincipalInfo ? Some(*mPrincipalInfo) : Nothing(); 133 134 nsAutoCString bodyBlobURISpec(BodyBlobURISpec()); 135 nsAutoString bodyLocalPath(BodyLocalPath()); 136 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()); 145 } 146 147 void InternalResponse::ToChildToParentInternalResponse( 148 ChildToParentInternalResponse* aIPCResponse, 149 mozilla::ipc::PBackgroundChild* aManager) { 150 *aIPCResponse = ChildToParentInternalResponse(GetMetadata(), Nothing(), 151 UNKNOWN_BODY_SIZE, Nothing()); 152 153 nsCOMPtr<nsIInputStream> body; 154 int64_t bodySize; 155 GetUnfilteredBody(getter_AddRefs(body), &bodySize); 156 157 if (body) { 158 aIPCResponse->body().emplace(ChildToParentStream()); 159 aIPCResponse->bodySize() = bodySize; 160 161 DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream( 162 body.forget(), aIPCResponse->body()->stream(), /* aAllowLazy */ false); 163 MOZ_ASSERT(ok); 164 } 165 166 nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody(); 167 if (alternativeBody) { 168 aIPCResponse->alternativeBody().emplace(ChildToParentStream()); 169 170 DebugOnly<bool> ok = mozilla::ipc::SerializeIPCStream( 171 alternativeBody.forget(), aIPCResponse->alternativeBody()->stream(), 172 /* aAllowLazy */ false); 173 MOZ_ASSERT(ok); 174 } 175 } 176 177 ParentToParentInternalResponse 178 InternalResponse::ToParentToParentInternalResponse() { 179 ParentToParentInternalResponse result(GetMetadata(), Nothing(), 180 UNKNOWN_BODY_SIZE, Nothing()); 181 182 nsCOMPtr<nsIInputStream> body; 183 int64_t bodySize; 184 GetUnfilteredBody(getter_AddRefs(body), &bodySize); 185 186 if (body) { 187 result.body() = Some(ToParentToParentStream(WrapNotNull(body), bodySize)); 188 result.bodySize() = bodySize; 189 } 190 191 nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody(); 192 if (alternativeBody) { 193 result.alternativeBody() = Some(ToParentToParentStream( 194 WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE)); 195 } 196 197 return result; 198 } 199 200 ParentToChildInternalResponse 201 InternalResponse::ToParentToChildInternalResponse() { 202 ParentToChildInternalResponse result(GetMetadata(), Nothing(), 203 UNKNOWN_BODY_SIZE, Nothing()); 204 205 nsCOMPtr<nsIInputStream> body; 206 int64_t bodySize; 207 GetUnfilteredBody(getter_AddRefs(body), &bodySize); 208 209 if (body) { 210 ParentToChildStream bodyStream = 211 ToParentToChildStream(WrapNotNull(body), bodySize, 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; 219 } 220 } 221 222 nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody(); 223 if (alternativeBody) { 224 ParentToChildStream alterBodyStream = ToParentToChildStream( 225 WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE, mSerializeAsLazy); 226 // The body stream can fail to serialize as an IPCStream. In the case, the 227 // IPCStream's type would be T__None. Don't set up IPCInternalResponse's 228 // body with the failed IPCStream. 229 if (mSerializeAsLazy || alterBodyStream.get_IPCStream().stream().type() != 230 mozilla::ipc::InputStreamParams::T__None) { 231 result.alternativeBody() = Some(alterBodyStream); 232 } 233 } 234 235 return result; 236 } 237 238 SafeRefPtr<InternalResponse> InternalResponse::Clone(CloneType aCloneType) { 239 SafeRefPtr<InternalResponse> clone = CreateIncompleteCopy(); 240 clone->mCloned = (mCloned = true); 241 242 clone->mHeaders = new InternalHeaders(*mHeaders); 243 244 // Make sure the clone response will have the same padding size. 245 clone->mPaddingInfo = mPaddingInfo; 246 clone->mPaddingSize = mPaddingSize; 247 248 clone->mCacheInfoChannel = mCacheInfoChannel; 249 clone->mCredentialsMode = mCredentialsMode; 250 251 if (mWrappedResponse) { 252 clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType); 253 MOZ_ASSERT(!mBody); 254 return clone; 255 } 256 257 if (!mBody || aCloneType == eDontCloneInputStream) { 258 return clone; 259 } 260 261 nsCOMPtr<nsIInputStream> clonedBody; 262 nsCOMPtr<nsIInputStream> replacementBody; 263 264 nsresult rv = NS_CloneInputStream(mBody, getter_AddRefs(clonedBody), 265 getter_AddRefs(replacementBody)); 266 if (NS_WARN_IF(NS_FAILED(rv))) { 267 return nullptr; 268 } 269 270 clone->mBody.swap(clonedBody); 271 if (replacementBody) { 272 mBody.swap(replacementBody); 273 } 274 275 return clone; 276 } 277 278 SafeRefPtr<InternalResponse> InternalResponse::BasicResponse() { 279 MOZ_ASSERT(!mWrappedResponse, 280 "Can't BasicResponse a already wrapped response"); 281 SafeRefPtr<InternalResponse> basic = CreateIncompleteCopy(); 282 basic->mType = ResponseType::Basic; 283 basic->mHeaders = InternalHeaders::BasicHeaders(Headers()); 284 basic->mWrappedResponse = SafeRefPtrFromThis(); 285 return basic; 286 } 287 288 SafeRefPtr<InternalResponse> InternalResponse::CORSResponse() { 289 MOZ_ASSERT(!mWrappedResponse, 290 "Can't CORSResponse a already wrapped response"); 291 SafeRefPtr<InternalResponse> cors = CreateIncompleteCopy(); 292 cors->mType = ResponseType::Cors; 293 cors->mHeaders = InternalHeaders::CORSHeaders(Headers(), mCredentialsMode); 294 cors->mWrappedResponse = SafeRefPtrFromThis(); 295 return cors; 296 } 297 298 uint32_t InternalResponse::GetPaddingInfo() { 299 // If it's an opaque response, the paddingInfo should be generated only when 300 // paddingSize is unknown size. 301 // If it's not, the paddingInfo should be nothing and the paddingSize should 302 // be unknown size. 303 MOZ_DIAGNOSTIC_ASSERT( 304 (mType == ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE && 305 mPaddingInfo.isSome()) || 306 (mType == ResponseType::Opaque && mPaddingSize != UNKNOWN_PADDING_SIZE && 307 mPaddingInfo.isNothing()) || 308 (mType != ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE && 309 mPaddingInfo.isNothing())); 310 return mPaddingInfo.isSome() ? mPaddingInfo.ref() : 0; 311 } 312 313 nsresult InternalResponse::GeneratePaddingInfo() { 314 MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque); 315 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE); 316 317 // Utilize random generator to generator a random number 318 nsresult rv; 319 uint32_t randomNumber = 0; 320 nsCOMPtr<nsIRandomGenerator> randomGenerator = 321 do_GetService("@mozilla.org/security/random-generator;1", &rv); 322 if (NS_WARN_IF(NS_FAILED(rv))) { 323 Maybe<uint64_t> maybeRandomNum = RandomUint64(); 324 if (maybeRandomNum.isSome()) { 325 mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber)); 326 return NS_OK; 327 } 328 return rv; 329 } 330 331 MOZ_DIAGNOSTIC_ASSERT(randomGenerator); 332 333 rv = randomGenerator->GenerateRandomBytesInto(randomNumber); 334 if (NS_WARN_IF(NS_FAILED(rv))) { 335 Maybe<uint64_t> maybeRandomNum = RandomUint64(); 336 if (maybeRandomNum.isSome()) { 337 mPaddingInfo.emplace(uint32_t(maybeRandomNum.value() % kMaxRandomNumber)); 338 return NS_OK; 339 } 340 return rv; 341 } 342 343 mPaddingInfo.emplace(randomNumber % kMaxRandomNumber); 344 345 return rv; 346 } 347 348 int64_t InternalResponse::GetPaddingSize() { 349 // We initialize padding size to an unknown size (-1). After cached, we only 350 // pad opaque response. Opaque response's padding size might be unknown before 351 // cached. 352 MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque || 353 mPaddingSize == UNKNOWN_PADDING_SIZE); 354 MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE || 355 mPaddingSize >= 0); 356 357 return mPaddingSize; 358 } 359 360 void InternalResponse::SetPaddingSize(int64_t aPaddingSize) { 361 // We should only pad the opaque response. 362 MOZ_DIAGNOSTIC_ASSERT( 363 (mType == ResponseType::Opaque) != 364 (aPaddingSize == InternalResponse::UNKNOWN_PADDING_SIZE)); 365 MOZ_DIAGNOSTIC_ASSERT(aPaddingSize == UNKNOWN_PADDING_SIZE || 366 aPaddingSize >= 0); 367 368 mPaddingSize = aPaddingSize; 369 } 370 371 void InternalResponse::SetPrincipalInfo( 372 UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo) { 373 mPrincipalInfo = std::move(aPrincipalInfo); 374 } 375 376 LoadTainting InternalResponse::GetTainting() const { 377 switch (mType) { 378 case ResponseType::Cors: 379 return LoadTainting::CORS; 380 case ResponseType::Opaque: 381 return LoadTainting::Opaque; 382 default: 383 return LoadTainting::Basic; 384 } 385 } 386 387 SafeRefPtr<InternalResponse> InternalResponse::Unfiltered() { 388 SafeRefPtr<InternalResponse> ref = mWrappedResponse.clonePtr(); 389 if (!ref) { 390 ref = SafeRefPtrFromThis(); 391 } 392 return ref; 393 } 394 395 SafeRefPtr<InternalResponse> InternalResponse::OpaqueResponse() { 396 MOZ_ASSERT(!mWrappedResponse, 397 "Can't OpaqueResponse a already wrapped response"); 398 SafeRefPtr<InternalResponse> response = 399 MakeSafeRefPtr<InternalResponse>(0, ""_ns); 400 response->mType = ResponseType::Opaque; 401 response->mChannelInfo = mChannelInfo; 402 if (mPrincipalInfo) { 403 response->mPrincipalInfo = 404 MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo); 405 } 406 response->mWrappedResponse = SafeRefPtrFromThis(); 407 return response; 408 } 409 410 SafeRefPtr<InternalResponse> InternalResponse::OpaqueRedirectResponse() { 411 MOZ_ASSERT(!mWrappedResponse, 412 "Can't OpaqueRedirectResponse a already wrapped response"); 413 MOZ_ASSERT(!mURLList.IsEmpty(), 414 "URLList should not be emtpy for internalResponse"); 415 SafeRefPtr<InternalResponse> response = OpaqueResponse(); 416 response->mType = ResponseType::Opaqueredirect; 417 response->mURLList = mURLList.Clone(); 418 return response; 419 } 420 421 SafeRefPtr<InternalResponse> InternalResponse::CreateIncompleteCopy() { 422 SafeRefPtr<InternalResponse> copy = 423 MakeSafeRefPtr<InternalResponse>(mStatus, mStatusText); 424 copy->mType = mType; 425 copy->mURLList = mURLList.Clone(); 426 copy->mChannelInfo = mChannelInfo; 427 if (mPrincipalInfo) { 428 copy->mPrincipalInfo = 429 MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo); 430 } 431 return copy; 432 } 433 434 ParentToChildInternalResponse ToParentToChild( 435 const ParentToParentInternalResponse& aResponse) { 436 ParentToChildInternalResponse result(aResponse.metadata(), Nothing(), 437 aResponse.bodySize(), Nothing()); 438 439 if (aResponse.body().isSome()) { 440 result.body() = Some( 441 ToParentToChildStream(aResponse.body().ref(), aResponse.bodySize())); 442 } 443 if (aResponse.alternativeBody().isSome()) { 444 result.alternativeBody() = 445 Some(ToParentToChildStream(aResponse.alternativeBody().ref(), 446 InternalResponse::UNKNOWN_BODY_SIZE)); 447 } 448 449 return result; 450 } 451 452 } // namespace mozilla::dom