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