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/dom/InternalHeaders.h"
11 #include "mozilla/dom/cache/CacheTypes.h"
12 #include "mozilla/ipc/PBackgroundSharedTypes.h"
13 #include "mozilla/ipc/IPCStreamUtils.h"
14 #include "nsIRandomGenerator.h"
15 #include "nsIURI.h"
16 #include "nsStreamUtils.h"
17 
18 namespace mozilla {
19 namespace dom {
20 
21 namespace {
22 
23 // Const variable for generate padding size
24 // XXX This will be tweaked to something more meaningful in Bug 1383656.
25 const uint32_t kMaxRandomNumber = 102400;
26 
27 }  // namespace
28 
InternalResponse(uint16_t aStatus,const nsACString & aStatusText)29 InternalResponse::InternalResponse(uint16_t aStatus,
30                                    const nsACString& aStatusText)
31     : mType(ResponseType::Default),
32       mStatus(aStatus),
33       mStatusText(aStatusText),
34       mHeaders(new InternalHeaders(HeadersGuardEnum::Response)),
35       mBodySize(UNKNOWN_BODY_SIZE),
36       mPaddingSize(UNKNOWN_PADDING_SIZE),
37       mErrorCode(NS_OK) {}
38 
FromIPC(const IPCInternalResponse & aIPCResponse)39 already_AddRefed<InternalResponse> InternalResponse::FromIPC(
40     const IPCInternalResponse& aIPCResponse) {
41   if (aIPCResponse.type() == ResponseType::Error) {
42     return InternalResponse::NetworkError(aIPCResponse.errorCode());
43   }
44 
45   RefPtr<InternalResponse> response =
46       new InternalResponse(aIPCResponse.status(), aIPCResponse.statusText());
47 
48   response->SetURLList(aIPCResponse.urlList());
49 
50   response->mHeaders =
51       new InternalHeaders(aIPCResponse.headers(), aIPCResponse.headersGuard());
52 
53   response->InitChannelInfo(aIPCResponse.channelInfo());
54   if (aIPCResponse.principalInfo().type() ==
55       mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
56     UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(
57         aIPCResponse.principalInfo().get_PrincipalInfo()));
58     response->SetPrincipalInfo(Move(info));
59   }
60 
61   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aIPCResponse.body());
62   response->SetBody(stream, aIPCResponse.bodySize());
63 
64   switch (aIPCResponse.type()) {
65     case ResponseType::Basic:
66       response = response->BasicResponse();
67       break;
68     case ResponseType::Cors:
69       response = response->CORSResponse();
70       break;
71     case ResponseType::Default:
72       break;
73     case ResponseType::Opaque:
74       response = response->OpaqueResponse();
75       break;
76     case ResponseType::Opaqueredirect:
77       response = response->OpaqueRedirectResponse();
78       break;
79     default:
80       MOZ_CRASH("Unexpected ResponseType!");
81   }
82   MOZ_ASSERT(response);
83 
84   return response.forget();
85 }
86 
~InternalResponse()87 InternalResponse::~InternalResponse() {}
88 
89 template void InternalResponse::ToIPC<nsIContentParent>(
90     IPCInternalResponse* aIPCResponse, nsIContentParent* aManager,
91     UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
92 template void InternalResponse::ToIPC<nsIContentChild>(
93     IPCInternalResponse* aIPCResponse, nsIContentChild* aManager,
94     UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
95 template void InternalResponse::ToIPC<mozilla::ipc::PBackgroundParent>(
96     IPCInternalResponse* aIPCResponse,
97     mozilla::ipc::PBackgroundParent* aManager,
98     UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
99 template void InternalResponse::ToIPC<mozilla::ipc::PBackgroundChild>(
100     IPCInternalResponse* aIPCResponse, mozilla::ipc::PBackgroundChild* aManager,
101     UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
102 
103 template <typename M>
ToIPC(IPCInternalResponse * aIPCResponse,M * aManager,UniquePtr<mozilla::ipc::AutoIPCStream> & aAutoStream)104 void InternalResponse::ToIPC(
105     IPCInternalResponse* aIPCResponse, M* aManager,
106     UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream) {
107   MOZ_ASSERT(aIPCResponse);
108   aIPCResponse->type() = mType;
109   aIPCResponse->urlList() = mURLList;
110   aIPCResponse->status() = GetUnfilteredStatus();
111   aIPCResponse->statusText() = GetUnfilteredStatusText();
112 
113   mHeaders->ToIPC(aIPCResponse->headers(), aIPCResponse->headersGuard());
114 
115   aIPCResponse->channelInfo() = mChannelInfo.AsIPCChannelInfo();
116   if (mPrincipalInfo) {
117     aIPCResponse->principalInfo() = *mPrincipalInfo;
118   } else {
119     aIPCResponse->principalInfo() = void_t();
120   }
121 
122   nsCOMPtr<nsIInputStream> body;
123   int64_t bodySize;
124   GetUnfilteredBody(getter_AddRefs(body), &bodySize);
125 
126   if (body) {
127     aAutoStream.reset(new mozilla::ipc::AutoIPCStream(aIPCResponse->body()));
128     DebugOnly<bool> ok = aAutoStream->Serialize(body, aManager);
129     MOZ_ASSERT(ok);
130   } else {
131     aIPCResponse->body() = void_t();
132   }
133 
134   aIPCResponse->bodySize() = bodySize;
135 }
136 
Clone(CloneType aCloneType)137 already_AddRefed<InternalResponse> InternalResponse::Clone(
138     CloneType aCloneType) {
139   RefPtr<InternalResponse> clone = CreateIncompleteCopy();
140 
141   clone->mHeaders = new InternalHeaders(*mHeaders);
142 
143   // Make sure the clone response will have the same padding size.
144   clone->mPaddingInfo = mPaddingInfo;
145   clone->mPaddingSize = mPaddingSize;
146 
147   if (mWrappedResponse) {
148     clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
149     MOZ_ASSERT(!mBody);
150     return clone.forget();
151   }
152 
153   if (!mBody || aCloneType == eDontCloneInputStream) {
154     return clone.forget();
155   }
156 
157   nsCOMPtr<nsIInputStream> clonedBody;
158   nsCOMPtr<nsIInputStream> replacementBody;
159 
160   nsresult rv = NS_CloneInputStream(mBody, getter_AddRefs(clonedBody),
161                                     getter_AddRefs(replacementBody));
162   if (NS_WARN_IF(NS_FAILED(rv))) {
163     return nullptr;
164   }
165 
166   clone->mBody.swap(clonedBody);
167   if (replacementBody) {
168     mBody.swap(replacementBody);
169   }
170 
171   return clone.forget();
172 }
173 
BasicResponse()174 already_AddRefed<InternalResponse> InternalResponse::BasicResponse() {
175   MOZ_ASSERT(!mWrappedResponse,
176              "Can't BasicResponse a already wrapped response");
177   RefPtr<InternalResponse> basic = CreateIncompleteCopy();
178   basic->mType = ResponseType::Basic;
179   basic->mHeaders = InternalHeaders::BasicHeaders(Headers());
180   basic->mWrappedResponse = this;
181   return basic.forget();
182 }
183 
CORSResponse()184 already_AddRefed<InternalResponse> InternalResponse::CORSResponse() {
185   MOZ_ASSERT(!mWrappedResponse,
186              "Can't CORSResponse a already wrapped response");
187   RefPtr<InternalResponse> cors = CreateIncompleteCopy();
188   cors->mType = ResponseType::Cors;
189   cors->mHeaders = InternalHeaders::CORSHeaders(Headers());
190   cors->mWrappedResponse = this;
191   return cors.forget();
192 }
193 
GetPaddingInfo()194 uint32_t InternalResponse::GetPaddingInfo() {
195   // If it's an opaque response, the paddingInfo should be generated only when
196   // paddingSize is unknown size.
197   // If it's not, the paddingInfo should be nothing and the paddingSize should
198   // be unknown size.
199   MOZ_DIAGNOSTIC_ASSERT(
200       (mType == ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE &&
201        mPaddingInfo.isSome()) ||
202       (mType == ResponseType::Opaque && mPaddingSize != UNKNOWN_PADDING_SIZE &&
203        mPaddingInfo.isNothing()) ||
204       (mType != ResponseType::Opaque && mPaddingSize == UNKNOWN_PADDING_SIZE &&
205        mPaddingInfo.isNothing()));
206   return mPaddingInfo.isSome() ? mPaddingInfo.ref() : 0;
207 }
208 
GeneratePaddingInfo()209 nsresult InternalResponse::GeneratePaddingInfo() {
210   MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque);
211   MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE);
212 
213   // Utilize random generator to generator a random number
214   nsresult rv;
215   uint32_t randomNumber = 0;
216   nsCOMPtr<nsIRandomGenerator> randomGenerator =
217       do_GetService("@mozilla.org/security/random-generator;1", &rv);
218   if (NS_WARN_IF(NS_FAILED(rv))) {
219     return rv;
220   }
221 
222   MOZ_DIAGNOSTIC_ASSERT(randomGenerator);
223 
224   uint8_t* buffer;
225   rv = randomGenerator->GenerateRandomBytes(sizeof(randomNumber), &buffer);
226   if (NS_WARN_IF(NS_FAILED(rv))) {
227     return rv;
228   }
229 
230   memcpy(&randomNumber, buffer, sizeof(randomNumber));
231   free(buffer);
232 
233   mPaddingInfo.emplace(randomNumber % kMaxRandomNumber);
234 
235   return rv;
236 }
237 
GetPaddingSize()238 int64_t InternalResponse::GetPaddingSize() {
239   // We initialize padding size to an unknown size (-1). After cached, we only
240   // pad opaque response. Opaque response's padding size might be unknown before
241   // cached.
242   MOZ_DIAGNOSTIC_ASSERT(mType == ResponseType::Opaque ||
243                         mPaddingSize == UNKNOWN_PADDING_SIZE);
244   MOZ_DIAGNOSTIC_ASSERT(mPaddingSize == UNKNOWN_PADDING_SIZE ||
245                         mPaddingSize >= 0);
246 
247   return mPaddingSize;
248 }
249 
SetPaddingSize(int64_t aPaddingSize)250 void InternalResponse::SetPaddingSize(int64_t aPaddingSize) {
251   // We should only pad the opaque response.
252   MOZ_DIAGNOSTIC_ASSERT(
253       (mType == ResponseType::Opaque) !=
254       (aPaddingSize == InternalResponse::UNKNOWN_PADDING_SIZE));
255   MOZ_DIAGNOSTIC_ASSERT(aPaddingSize == UNKNOWN_PADDING_SIZE ||
256                         aPaddingSize >= 0);
257 
258   mPaddingSize = aPaddingSize;
259 }
260 
SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)261 void InternalResponse::SetPrincipalInfo(
262     UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo) {
263   mPrincipalInfo = Move(aPrincipalInfo);
264 }
265 
GetTainting() const266 LoadTainting InternalResponse::GetTainting() const {
267   switch (mType) {
268     case ResponseType::Cors:
269       return LoadTainting::CORS;
270     case ResponseType::Opaque:
271       return LoadTainting::Opaque;
272     default:
273       return LoadTainting::Basic;
274   }
275 }
276 
Unfiltered()277 already_AddRefed<InternalResponse> InternalResponse::Unfiltered() {
278   RefPtr<InternalResponse> ref = mWrappedResponse;
279   if (!ref) {
280     ref = this;
281   }
282   return ref.forget();
283 }
284 
OpaqueResponse()285 already_AddRefed<InternalResponse> InternalResponse::OpaqueResponse() {
286   MOZ_ASSERT(!mWrappedResponse,
287              "Can't OpaqueResponse a already wrapped response");
288   RefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
289   response->mType = ResponseType::Opaque;
290   response->mTerminationReason = mTerminationReason;
291   response->mChannelInfo = mChannelInfo;
292   if (mPrincipalInfo) {
293     response->mPrincipalInfo =
294         MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
295   }
296   response->mWrappedResponse = this;
297   return response.forget();
298 }
299 
OpaqueRedirectResponse()300 already_AddRefed<InternalResponse> InternalResponse::OpaqueRedirectResponse() {
301   MOZ_ASSERT(!mWrappedResponse,
302              "Can't OpaqueRedirectResponse a already wrapped response");
303   MOZ_ASSERT(!mURLList.IsEmpty(),
304              "URLList should not be emtpy for internalResponse");
305   RefPtr<InternalResponse> response = OpaqueResponse();
306   response->mType = ResponseType::Opaqueredirect;
307   response->mURLList = mURLList;
308   return response.forget();
309 }
310 
CreateIncompleteCopy()311 already_AddRefed<InternalResponse> InternalResponse::CreateIncompleteCopy() {
312   RefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
313   copy->mType = mType;
314   copy->mTerminationReason = mTerminationReason;
315   copy->mURLList = mURLList;
316   copy->mChannelInfo = mChannelInfo;
317   if (mPrincipalInfo) {
318     copy->mPrincipalInfo =
319         MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
320   }
321   return copy.forget();
322 }
323 
324 }  // namespace dom
325 }  // namespace mozilla
326