Line data Source code
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 "mozilla/dom/cache/TypeUtils.h"
8 :
9 : #include "mozilla/Unused.h"
10 : #include "mozilla/dom/CacheBinding.h"
11 : #include "mozilla/dom/InternalRequest.h"
12 : #include "mozilla/dom/Request.h"
13 : #include "mozilla/dom/Response.h"
14 : #include "mozilla/dom/cache/CacheTypes.h"
15 : #include "mozilla/dom/cache/ReadStream.h"
16 : #include "mozilla/ipc/BackgroundChild.h"
17 : #include "mozilla/ipc/PBackgroundChild.h"
18 : #include "mozilla/ipc/PFileDescriptorSetChild.h"
19 : #include "mozilla/ipc/InputStreamUtils.h"
20 : #include "nsCOMPtr.h"
21 : #include "nsIAsyncInputStream.h"
22 : #include "nsIAsyncOutputStream.h"
23 : #include "nsIIPCSerializableInputStream.h"
24 : #include "nsQueryObject.h"
25 : #include "nsPromiseFlatString.h"
26 : #include "nsStreamUtils.h"
27 : #include "nsString.h"
28 : #include "nsURLParsers.h"
29 : #include "nsCRT.h"
30 : #include "nsHttp.h"
31 :
32 : namespace mozilla {
33 : namespace dom {
34 : namespace cache {
35 :
36 : using mozilla::ipc::AutoIPCStream;
37 : using mozilla::ipc::BackgroundChild;
38 : using mozilla::ipc::FileDescriptor;
39 : using mozilla::ipc::PBackgroundChild;
40 : using mozilla::ipc::PFileDescriptorSetChild;
41 :
42 : namespace {
43 :
44 : static bool
45 0 : HasVaryStar(mozilla::dom::InternalHeaders* aHeaders)
46 : {
47 0 : nsCString varyHeaders;
48 0 : ErrorResult rv;
49 0 : aHeaders->Get(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
50 0 : MOZ_ALWAYS_TRUE(!rv.Failed());
51 :
52 0 : char* rawBuffer = varyHeaders.BeginWriting();
53 0 : char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
54 0 : for (; token;
55 0 : token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
56 0 : nsDependentCString header(token);
57 0 : if (header.EqualsLiteral("*")) {
58 0 : return true;
59 : }
60 : }
61 0 : return false;
62 : }
63 :
64 : void
65 0 : ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
66 : {
67 0 : MOZ_DIAGNOSTIC_ASSERT(aHeaders);
68 :
69 0 : AutoTArray<InternalHeaders::Entry, 16> entryList;
70 0 : aHeaders->GetEntries(entryList);
71 :
72 0 : for (uint32_t i = 0; i < entryList.Length(); ++i) {
73 0 : InternalHeaders::Entry& entry = entryList[i];
74 0 : aOut.AppendElement(HeadersEntry(entry.mName, entry.mValue));
75 : }
76 0 : }
77 :
78 : } // namespace
79 :
80 : already_AddRefed<InternalRequest>
81 0 : TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
82 : BodyAction aBodyAction, ErrorResult& aRv)
83 : {
84 0 : if (aIn.IsRequest()) {
85 0 : Request& request = aIn.GetAsRequest();
86 :
87 : // Check and set bodyUsed flag immediately because its on Request
88 : // instead of InternalRequest.
89 0 : CheckAndSetBodyUsed(&request, aBodyAction, aRv);
90 0 : if (aRv.Failed()) { return nullptr; }
91 :
92 0 : return request.GetInternalRequest();
93 : }
94 :
95 0 : return ToInternalRequest(aIn.GetAsUSVString(), aRv);
96 : }
97 :
98 : already_AddRefed<InternalRequest>
99 0 : TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
100 : BodyAction aBodyAction, ErrorResult& aRv)
101 : {
102 :
103 0 : if (aIn.IsRequest()) {
104 0 : RefPtr<Request> request = aIn.GetAsRequest().get();
105 :
106 : // Check and set bodyUsed flag immediately because its on Request
107 : // instead of InternalRequest.
108 0 : CheckAndSetBodyUsed(request, aBodyAction, aRv);
109 0 : if (aRv.Failed()) { return nullptr; }
110 :
111 0 : return request->GetInternalRequest();
112 : }
113 :
114 0 : return ToInternalRequest(aIn.GetAsUSVString(), aRv);
115 : }
116 :
117 : void
118 0 : TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
119 : BodyAction aBodyAction, SchemeAction aSchemeAction,
120 : nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
121 : ErrorResult& aRv)
122 : {
123 0 : MOZ_DIAGNOSTIC_ASSERT(aIn);
124 0 : aIn->GetMethod(aOut.method());
125 0 : nsCString url(aIn->GetURLWithoutFragment());
126 : bool schemeValid;
127 0 : ProcessURL(url, &schemeValid, &aOut.urlWithoutQuery(), &aOut.urlQuery(), aRv);
128 0 : if (aRv.Failed()) {
129 0 : return;
130 : }
131 0 : if (!schemeValid) {
132 0 : if (aSchemeAction == TypeErrorOnInvalidScheme) {
133 0 : NS_ConvertUTF8toUTF16 urlUTF16(url);
134 0 : aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Request"),
135 0 : urlUTF16);
136 0 : return;
137 : }
138 : }
139 0 : aOut.urlFragment() = aIn->GetFragment();
140 :
141 0 : aIn->GetReferrer(aOut.referrer());
142 0 : aOut.referrerPolicy() = aIn->ReferrerPolicy_();
143 0 : RefPtr<InternalHeaders> headers = aIn->Headers();
144 0 : MOZ_DIAGNOSTIC_ASSERT(headers);
145 0 : ToHeadersEntryList(aOut.headers(), headers);
146 0 : aOut.headersGuard() = headers->Guard();
147 0 : aOut.mode() = aIn->Mode();
148 0 : aOut.credentials() = aIn->GetCredentialsMode();
149 0 : aOut.contentPolicyType() = aIn->ContentPolicyType();
150 0 : aOut.requestCache() = aIn->GetCacheMode();
151 0 : aOut.requestRedirect() = aIn->GetRedirectMode();
152 :
153 0 : aOut.integrity() = aIn->GetIntegrity();
154 :
155 0 : if (aBodyAction == IgnoreBody) {
156 0 : aOut.body() = void_t();
157 0 : return;
158 : }
159 :
160 : // BodyUsed flag is checked and set previously in ToInternalRequest()
161 :
162 0 : nsCOMPtr<nsIInputStream> stream;
163 0 : aIn->GetBody(getter_AddRefs(stream));
164 0 : SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
165 0 : if (NS_WARN_IF(aRv.Failed())) {
166 0 : return;
167 : }
168 : }
169 :
170 : void
171 0 : TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
172 : InternalResponse& aIn, ErrorResult& aRv)
173 : {
174 0 : aOut.type() = aIn.Type();
175 :
176 0 : aIn.GetUnfilteredURLList(aOut.urlList());
177 0 : AutoTArray<nsCString, 4> urlList;
178 0 : aIn.GetURLList(urlList);
179 :
180 0 : for (uint32_t i = 0; i < aOut.urlList().Length(); i++) {
181 0 : MOZ_DIAGNOSTIC_ASSERT(!aOut.urlList()[i].IsEmpty());
182 : // Pass all Response URL schemes through... The spec only requires we take
183 : // action on invalid schemes for Request objects.
184 0 : ProcessURL(aOut.urlList()[i], nullptr, nullptr, nullptr, aRv);
185 : }
186 :
187 0 : aOut.status() = aIn.GetUnfilteredStatus();
188 0 : aOut.statusText() = aIn.GetUnfilteredStatusText();
189 0 : RefPtr<InternalHeaders> headers = aIn.UnfilteredHeaders();
190 0 : MOZ_DIAGNOSTIC_ASSERT(headers);
191 0 : if (HasVaryStar(headers)) {
192 0 : aRv.ThrowTypeError<MSG_RESPONSE_HAS_VARY_STAR>();
193 0 : return;
194 : }
195 0 : ToHeadersEntryList(aOut.headers(), headers);
196 0 : aOut.headersGuard() = headers->Guard();
197 0 : aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
198 0 : if (aIn.GetPrincipalInfo()) {
199 0 : aOut.principalInfo() = *aIn.GetPrincipalInfo();
200 : } else {
201 0 : aOut.principalInfo() = void_t();
202 : }
203 : }
204 :
205 : void
206 0 : TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
207 : nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
208 : ErrorResult& aRv)
209 : {
210 0 : if (aIn.BodyUsed()) {
211 0 : aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
212 0 : return;
213 : }
214 :
215 0 : RefPtr<InternalResponse> ir = aIn.GetInternalResponse();
216 0 : ToCacheResponseWithoutBody(aOut, *ir, aRv);
217 0 : if (NS_WARN_IF(aRv.Failed())) {
218 0 : return;
219 : }
220 :
221 0 : nsCOMPtr<nsIInputStream> stream;
222 0 : ir->GetUnfilteredBody(getter_AddRefs(stream));
223 0 : if (stream) {
224 0 : aIn.SetBodyUsed();
225 : }
226 :
227 0 : SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
228 0 : if (NS_WARN_IF(aRv.Failed())) {
229 0 : return;
230 : }
231 : }
232 :
233 : // static
234 : void
235 0 : TypeUtils::ToCacheQueryParams(CacheQueryParams& aOut,
236 : const CacheQueryOptions& aIn)
237 : {
238 0 : aOut.ignoreSearch() = aIn.mIgnoreSearch;
239 0 : aOut.ignoreMethod() = aIn.mIgnoreMethod;
240 0 : aOut.ignoreVary() = aIn.mIgnoreVary;
241 0 : aOut.cacheNameSet() = aIn.mCacheName.WasPassed();
242 0 : if (aOut.cacheNameSet()) {
243 0 : aOut.cacheName() = aIn.mCacheName.Value();
244 : } else {
245 0 : aOut.cacheName() = NS_LITERAL_STRING("");
246 : }
247 0 : }
248 :
249 : already_AddRefed<Response>
250 0 : TypeUtils::ToResponse(const CacheResponse& aIn)
251 : {
252 0 : if (aIn.type() == ResponseType::Error) {
253 0 : RefPtr<InternalResponse> error = InternalResponse::NetworkError();
254 0 : RefPtr<Response> r = new Response(GetGlobalObject(), error);
255 0 : return r.forget();
256 : }
257 :
258 0 : RefPtr<InternalResponse> ir = new InternalResponse(aIn.status(),
259 0 : aIn.statusText());
260 0 : ir->SetURLList(aIn.urlList());
261 :
262 : RefPtr<InternalHeaders> internalHeaders =
263 0 : ToInternalHeaders(aIn.headers(), aIn.headersGuard());
264 0 : ErrorResult result;
265 :
266 : // Be careful to fill the headers before setting the guard in order to
267 : // correctly re-create the original headers.
268 0 : ir->Headers()->Fill(*internalHeaders, result);
269 0 : MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
270 0 : ir->Headers()->SetGuard(aIn.headersGuard(), result);
271 0 : MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
272 :
273 0 : ir->InitChannelInfo(aIn.channelInfo());
274 0 : if (aIn.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
275 0 : UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIn.principalInfo().get_PrincipalInfo()));
276 0 : ir->SetPrincipalInfo(Move(info));
277 : }
278 :
279 0 : nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
280 0 : ir->SetBody(stream, InternalResponse::UNKNOWN_BODY_SIZE);
281 :
282 0 : switch (aIn.type())
283 : {
284 : case ResponseType::Basic:
285 0 : ir = ir->BasicResponse();
286 0 : break;
287 : case ResponseType::Cors:
288 0 : ir = ir->CORSResponse();
289 0 : break;
290 : case ResponseType::Default:
291 0 : break;
292 : case ResponseType::Opaque:
293 0 : ir = ir->OpaqueResponse();
294 0 : break;
295 : case ResponseType::Opaqueredirect:
296 0 : ir = ir->OpaqueRedirectResponse();
297 0 : break;
298 : default:
299 0 : MOZ_CRASH("Unexpected ResponseType!");
300 : }
301 0 : MOZ_DIAGNOSTIC_ASSERT(ir);
302 :
303 0 : RefPtr<Response> ref = new Response(GetGlobalObject(), ir);
304 0 : return ref.forget();
305 : }
306 : already_AddRefed<InternalRequest>
307 0 : TypeUtils::ToInternalRequest(const CacheRequest& aIn)
308 : {
309 0 : nsAutoCString url(aIn.urlWithoutQuery());
310 0 : url.Append(aIn.urlQuery());
311 : RefPtr<InternalRequest> internalRequest =
312 0 : new InternalRequest(url, aIn.urlFragment());
313 0 : internalRequest->SetMethod(aIn.method());
314 0 : internalRequest->SetReferrer(aIn.referrer());
315 0 : internalRequest->SetReferrerPolicy(aIn.referrerPolicy());
316 0 : internalRequest->SetMode(aIn.mode());
317 0 : internalRequest->SetCredentialsMode(aIn.credentials());
318 0 : internalRequest->SetContentPolicyType(aIn.contentPolicyType());
319 0 : internalRequest->SetCacheMode(aIn.requestCache());
320 0 : internalRequest->SetRedirectMode(aIn.requestRedirect());
321 0 : internalRequest->SetIntegrity(aIn.integrity());
322 :
323 : RefPtr<InternalHeaders> internalHeaders =
324 0 : ToInternalHeaders(aIn.headers(), aIn.headersGuard());
325 0 : ErrorResult result;
326 :
327 : // Be careful to fill the headers before setting the guard in order to
328 : // correctly re-create the original headers.
329 0 : internalRequest->Headers()->Fill(*internalHeaders, result);
330 0 : MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
331 :
332 0 : internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
333 0 : MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
334 :
335 0 : nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
336 :
337 0 : internalRequest->SetBody(stream);
338 :
339 0 : return internalRequest.forget();
340 : }
341 :
342 : already_AddRefed<Request>
343 0 : TypeUtils::ToRequest(const CacheRequest& aIn)
344 : {
345 0 : RefPtr<InternalRequest> internalRequest = ToInternalRequest(aIn);
346 0 : RefPtr<Request> request = new Request(GetGlobalObject(), internalRequest);
347 0 : return request.forget();
348 : }
349 :
350 : // static
351 : already_AddRefed<InternalHeaders>
352 0 : TypeUtils::ToInternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
353 : HeadersGuardEnum aGuard)
354 : {
355 0 : nsTArray<InternalHeaders::Entry> entryList(aHeadersEntryList.Length());
356 :
357 0 : for (uint32_t i = 0; i < aHeadersEntryList.Length(); ++i) {
358 0 : const HeadersEntry& headersEntry = aHeadersEntryList[i];
359 0 : entryList.AppendElement(InternalHeaders::Entry(headersEntry.name(),
360 0 : headersEntry.value()));
361 : }
362 :
363 0 : RefPtr<InternalHeaders> ref = new InternalHeaders(Move(entryList), aGuard);
364 0 : return ref.forget();
365 : }
366 :
367 : // Utility function to remove the fragment from a URL, check its scheme, and optionally
368 : // provide a URL without the query. We're not using nsIURL or URL to do this because
369 : // they require going to the main thread.
370 : // static
371 : void
372 0 : TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
373 : nsACString* aUrlWithoutQueryOut,nsACString* aUrlQueryOut,
374 : ErrorResult& aRv)
375 : {
376 0 : const nsCString& flatURL = PromiseFlatCString(aUrl);
377 0 : const char* url = flatURL.get();
378 :
379 : // off the main thread URL parsing using nsStdURLParser.
380 0 : nsCOMPtr<nsIURLParser> urlParser = new nsStdURLParser();
381 :
382 : uint32_t pathPos;
383 : int32_t pathLen;
384 : uint32_t schemePos;
385 : int32_t schemeLen;
386 0 : aRv = urlParser->ParseURL(url, flatURL.Length(), &schemePos, &schemeLen,
387 : nullptr, nullptr, // ignore authority
388 0 : &pathPos, &pathLen);
389 0 : if (NS_WARN_IF(aRv.Failed())) { return; }
390 :
391 0 : if (aSchemeValidOut) {
392 0 : nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
393 0 : *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") ||
394 0 : scheme.LowerCaseEqualsLiteral("https");
395 : }
396 :
397 : uint32_t queryPos;
398 : int32_t queryLen;
399 :
400 0 : aRv = urlParser->ParsePath(url + pathPos, flatURL.Length() - pathPos,
401 : nullptr, nullptr, // ignore filepath
402 : &queryPos, &queryLen,
403 0 : nullptr, nullptr);
404 0 : if (NS_WARN_IF(aRv.Failed())) {
405 0 : return;
406 : }
407 :
408 0 : if (!aUrlWithoutQueryOut) {
409 0 : return;
410 : }
411 :
412 0 : MOZ_DIAGNOSTIC_ASSERT(aUrlQueryOut);
413 :
414 0 : if (queryLen < 0) {
415 0 : *aUrlWithoutQueryOut = aUrl;
416 0 : *aUrlQueryOut = EmptyCString();
417 0 : return;
418 : }
419 :
420 : // ParsePath gives us query position relative to the start of the path
421 0 : queryPos += pathPos;
422 :
423 0 : *aUrlWithoutQueryOut = Substring(aUrl, 0, queryPos - 1);
424 0 : *aUrlQueryOut = Substring(aUrl, queryPos - 1, queryLen + 1);
425 : }
426 :
427 : void
428 0 : TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
429 : ErrorResult& aRv)
430 : {
431 0 : MOZ_DIAGNOSTIC_ASSERT(aRequest);
432 :
433 0 : if (aBodyAction == IgnoreBody) {
434 0 : return;
435 : }
436 :
437 0 : if (aRequest->BodyUsed()) {
438 0 : aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
439 0 : return;
440 : }
441 :
442 0 : nsCOMPtr<nsIInputStream> stream;
443 0 : aRequest->GetBody(getter_AddRefs(stream));
444 0 : if (stream) {
445 0 : aRequest->SetBodyUsed();
446 : }
447 : }
448 :
449 : already_AddRefed<InternalRequest>
450 0 : TypeUtils::ToInternalRequest(const nsAString& aIn, ErrorResult& aRv)
451 : {
452 0 : RequestOrUSVString requestOrString;
453 0 : requestOrString.SetAsUSVString().Rebind(aIn.Data(), aIn.Length());
454 :
455 : // Re-create a GlobalObject stack object so we can use webidl Constructors.
456 0 : AutoJSAPI jsapi;
457 0 : if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
458 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
459 0 : return nullptr;
460 : }
461 0 : JSContext* cx = jsapi.cx();
462 0 : GlobalObject global(cx, GetGlobalObject()->GetGlobalJSObject());
463 0 : MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
464 :
465 0 : RefPtr<Request> request = Request::Constructor(global, requestOrString,
466 0 : RequestInit(), aRv);
467 0 : if (NS_WARN_IF(aRv.Failed())) { return nullptr; }
468 :
469 0 : return request->GetInternalRequest();
470 : }
471 :
472 : void
473 0 : TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
474 : CacheReadStreamOrVoid* aStreamOut,
475 : nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
476 : ErrorResult& aRv)
477 : {
478 0 : *aStreamOut = void_t();
479 0 : if (!aStream) {
480 0 : return;
481 : }
482 :
483 0 : RefPtr<ReadStream> controlled = do_QueryObject(aStream);
484 0 : if (controlled) {
485 0 : controlled->Serialize(aStreamOut, aStreamCleanupList, aRv);
486 0 : return;
487 : }
488 :
489 0 : *aStreamOut = CacheReadStream();
490 0 : CacheReadStream& cacheStream = aStreamOut->get_CacheReadStream();
491 :
492 0 : cacheStream.controlChild() = nullptr;
493 0 : cacheStream.controlParent() = nullptr;
494 :
495 0 : UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(cacheStream.stream()));
496 0 : autoStream->Serialize(aStream, GetIPCManager());
497 :
498 0 : aStreamCleanupList.AppendElement(Move(autoStream));
499 : }
500 :
501 : } // namespace cache
502 : } // namespace dom
503 9 : } // namespace mozilla
|