tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TypeUtils.cpp (16197B)


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