tor-browser

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

AutoUtils.cpp (17289B)


      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/AutoUtils.h"
      8 
      9 #include "mozilla/dom/InternalHeaders.h"
     10 #include "mozilla/dom/InternalRequest.h"
     11 #include "mozilla/dom/cache/CacheParent.h"
     12 #include "mozilla/dom/cache/CacheStreamControlParent.h"
     13 #include "mozilla/dom/cache/PBoundStorageKeyParent.h"
     14 #include "mozilla/dom/cache/ReadStream.h"
     15 #include "mozilla/dom/cache/SavedTypes.h"
     16 #include "mozilla/dom/cache/StreamList.h"
     17 #include "mozilla/dom/cache/TypeUtils.h"
     18 #include "mozilla/ipc/IPCStreamUtils.h"
     19 #include "mozilla/ipc/PBackgroundParent.h"
     20 #include "nsCharSeparatedTokenizer.h"
     21 #include "nsHttp.h"
     22 
     23 using mozilla::Maybe;
     24 using mozilla::dom::cache::CacheReadStream;
     25 using mozilla::ipc::PBackgroundParent;
     26 
     27 namespace {
     28 
     29 enum CleanupAction { Forget, Delete };
     30 
     31 void CleanupChild(CacheReadStream& aReadStream, CleanupAction aAction) {
     32  // fds cleaned up by mStreamCleanupList
     33 }
     34 
     35 void CleanupChild(Maybe<CacheReadStream>& aMaybeReadStream,
     36                  CleanupAction aAction) {
     37  if (aMaybeReadStream.isNothing()) {
     38    return;
     39  }
     40 
     41  CleanupChild(aMaybeReadStream.ref(), aAction);
     42 }
     43 
     44 }  // namespace
     45 
     46 namespace mozilla::dom::cache {
     47 
     48 // --------------------------------------------
     49 
     50 AutoChildOpArgs::AutoChildOpArgs(TypeUtils* aTypeUtils,
     51                                 const CacheOpArgs& aOpArgs,
     52                                 uint32_t aEntryCount)
     53    : mTypeUtils(aTypeUtils), mOpArgs(aOpArgs), mSent(false) {
     54  MOZ_DIAGNOSTIC_ASSERT(mTypeUtils);
     55  MOZ_RELEASE_ASSERT(aEntryCount != 0);
     56  if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
     57    CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
     58    args.requestResponseList().SetCapacity(aEntryCount);
     59  } else {
     60    MOZ_DIAGNOSTIC_ASSERT(aEntryCount == 1);
     61  }
     62 }
     63 
     64 AutoChildOpArgs::~AutoChildOpArgs() {
     65  CleanupAction action = mSent ? Forget : Delete;
     66 
     67  switch (mOpArgs.type()) {
     68    case CacheOpArgs::TCacheMatchArgs: {
     69      CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
     70      CleanupChild(args.request().body(), action);
     71      break;
     72    }
     73    case CacheOpArgs::TCacheMatchAllArgs: {
     74      CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
     75      if (args.maybeRequest().isNothing()) {
     76        break;
     77      }
     78      CleanupChild(args.maybeRequest().ref().body(), action);
     79      break;
     80    }
     81    case CacheOpArgs::TCachePutAllArgs: {
     82      CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
     83      auto& list = args.requestResponseList();
     84      for (uint32_t i = 0; i < list.Length(); ++i) {
     85        CleanupChild(list[i].request().body(), action);
     86        CleanupChild(list[i].response().body(), action);
     87      }
     88      break;
     89    }
     90    case CacheOpArgs::TCacheDeleteArgs: {
     91      CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
     92      CleanupChild(args.request().body(), action);
     93      break;
     94    }
     95    case CacheOpArgs::TCacheKeysArgs: {
     96      CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
     97      if (args.maybeRequest().isNothing()) {
     98        break;
     99      }
    100      CleanupChild(args.maybeRequest().ref().body(), action);
    101      break;
    102    }
    103    case CacheOpArgs::TStorageMatchArgs: {
    104      StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
    105      CleanupChild(args.request().body(), action);
    106      break;
    107    }
    108    default:
    109      // Other types do not need cleanup
    110      break;
    111  }
    112 }
    113 
    114 void AutoChildOpArgs::Add(const InternalRequest& aRequest,
    115                          BodyAction aBodyAction, SchemeAction aSchemeAction,
    116                          ErrorResult& aRv) {
    117  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    118 
    119  switch (mOpArgs.type()) {
    120    case CacheOpArgs::TCacheMatchArgs: {
    121      CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
    122      mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
    123                                 aSchemeAction, aRv);
    124      break;
    125    }
    126    case CacheOpArgs::TCacheMatchAllArgs: {
    127      CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
    128      MOZ_DIAGNOSTIC_ASSERT(args.maybeRequest().isNothing());
    129      args.maybeRequest().emplace(CacheRequest());
    130      mTypeUtils->ToCacheRequest(args.maybeRequest().ref(), aRequest,
    131                                 aBodyAction, aSchemeAction, aRv);
    132      break;
    133    }
    134    case CacheOpArgs::TCacheDeleteArgs: {
    135      CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
    136      mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
    137                                 aSchemeAction, aRv);
    138      break;
    139    }
    140    case CacheOpArgs::TCacheKeysArgs: {
    141      CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
    142      MOZ_DIAGNOSTIC_ASSERT(args.maybeRequest().isNothing());
    143      args.maybeRequest().emplace(CacheRequest());
    144      mTypeUtils->ToCacheRequest(args.maybeRequest().ref(), aRequest,
    145                                 aBodyAction, aSchemeAction, aRv);
    146      break;
    147    }
    148    case CacheOpArgs::TStorageMatchArgs: {
    149      StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
    150      mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
    151                                 aSchemeAction, aRv);
    152      break;
    153    }
    154    default:
    155      MOZ_CRASH("Cache args type cannot send a Request!");
    156  }
    157 }
    158 
    159 namespace {
    160 
    161 bool MatchInPutList(const InternalRequest& aRequest,
    162                    const nsTArray<CacheRequestResponse>& aPutList) {
    163  // This method implements the SW spec QueryCache algorithm against an
    164  // in memory array of Request/Response objects.  This essentially the
    165  // same algorithm that is implemented in DBSchema.cpp.  Unfortunately
    166  // we cannot unify them because when operating against the real database
    167  // we don't want to load all request/response objects into memory.
    168 
    169  // Note, we can skip the check for a invalid request method because
    170  // Cache should only call into here with a GET or HEAD.
    171 #ifdef DEBUG
    172  nsAutoCString method;
    173  aRequest.GetMethod(method);
    174  MOZ_ASSERT(method.LowerCaseEqualsLiteral("get") ||
    175             method.LowerCaseEqualsLiteral("head"));
    176 #endif
    177 
    178  RefPtr<InternalHeaders> requestHeaders = aRequest.Headers();
    179 
    180  for (uint32_t i = 0; i < aPutList.Length(); ++i) {
    181    const CacheRequest& cachedRequest = aPutList[i].request();
    182    const CacheResponse& cachedResponse = aPutList[i].response();
    183 
    184    nsAutoCString url;
    185    aRequest.GetURL(url);
    186 
    187    nsAutoCString requestUrl(cachedRequest.urlWithoutQuery());
    188    requestUrl.Append(cachedRequest.urlQuery());
    189 
    190    // If the URLs don't match, then just skip to the next entry.
    191    if (url != requestUrl) {
    192      continue;
    193    }
    194 
    195    RefPtr<InternalHeaders> cachedRequestHeaders =
    196        TypeUtils::ToInternalHeaders(cachedRequest.headers());
    197 
    198    RefPtr<InternalHeaders> cachedResponseHeaders =
    199        TypeUtils::ToInternalHeaders(cachedResponse.headers());
    200 
    201    nsCString varyHeaders;
    202    ErrorResult rv;
    203    cachedResponseHeaders->Get("vary"_ns, varyHeaders, rv);
    204    MOZ_ALWAYS_TRUE(!rv.Failed());
    205 
    206    // Assume the vary headers match until we find a conflict
    207    bool varyHeadersMatch = true;
    208 
    209    for (const nsACString& header :
    210         nsCCharSeparatedTokenizer(varyHeaders, NS_HTTP_HEADER_SEP).ToRange()) {
    211      MOZ_DIAGNOSTIC_ASSERT(!header.EqualsLiteral("*"),
    212                            "We should have already caught this in "
    213                            "TypeUtils::ToPCacheResponseWithoutBody()");
    214 
    215      ErrorResult headerRv;
    216      nsAutoCString value;
    217      requestHeaders->Get(header, value, headerRv);
    218      if (NS_WARN_IF(headerRv.Failed())) {
    219        headerRv.SuppressException();
    220        MOZ_DIAGNOSTIC_ASSERT(value.IsEmpty());
    221      }
    222 
    223      nsAutoCString cachedValue;
    224      cachedRequestHeaders->Get(header, cachedValue, headerRv);
    225      if (NS_WARN_IF(headerRv.Failed())) {
    226        headerRv.SuppressException();
    227        MOZ_DIAGNOSTIC_ASSERT(cachedValue.IsEmpty());
    228      }
    229 
    230      if (value != cachedValue) {
    231        varyHeadersMatch = false;
    232        break;
    233      }
    234    }
    235 
    236    // URL was equal and all vary headers match!
    237    if (varyHeadersMatch) {
    238      return true;
    239    }
    240  }
    241 
    242  return false;
    243 }
    244 
    245 }  // namespace
    246 
    247 void AutoChildOpArgs::Add(JSContext* aCx, const InternalRequest& aRequest,
    248                          BodyAction aBodyAction, SchemeAction aSchemeAction,
    249                          Response& aResponse, ErrorResult& aRv) {
    250  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    251 
    252  switch (mOpArgs.type()) {
    253    case CacheOpArgs::TCachePutAllArgs: {
    254      CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
    255 
    256      // Throw an error if a request/response pair would mask another
    257      // request/response pair in the same PutAll operation.  This is
    258      // step 2.3.2.3 from the "Batch Cache Operations" spec algorithm.
    259      if (MatchInPutList(aRequest, args.requestResponseList())) {
    260        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    261        return;
    262      }
    263 
    264      MOZ_RELEASE_ASSERT(args.requestResponseList().Length() <
    265                         args.requestResponseList().Capacity());
    266 
    267      // The FileDescriptorSetChild asserts in its destructor that all fds have
    268      // been removed.  The copy constructor, however, simply duplicates the
    269      // fds without removing any.  This means each temporary and copy must be
    270      // explicitly cleaned up.
    271      //
    272      // Avoid a lot of this hassle by making sure we only create one here.  On
    273      // error we remove it.
    274      CacheRequestResponse& pair = *args.requestResponseList().AppendElement();
    275      pair.request().body() = Nothing();
    276      pair.response().body() = Nothing();
    277 
    278      mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
    279                                 aSchemeAction, aRv);
    280      if (!aRv.Failed()) {
    281        mTypeUtils->ToCacheResponse(aCx, pair.response(), aResponse, aRv);
    282      }
    283 
    284      if (aRv.Failed()) {
    285        CleanupChild(pair.request().body(), Delete);
    286        args.requestResponseList().RemoveLastElement();
    287      }
    288 
    289      break;
    290    }
    291    default:
    292      MOZ_CRASH("Cache args type cannot send a Request/Response pair!");
    293  }
    294 }
    295 
    296 const CacheOpArgs& AutoChildOpArgs::SendAsOpArgs() {
    297  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    298  mSent = true;
    299  return mOpArgs;
    300 }
    301 
    302 // --------------------------------------------
    303 
    304 AutoParentOpResult::AutoParentOpResult(const WeakRefParentType& aManager,
    305                                       const CacheOpResult& aOpResult,
    306                                       uint32_t aEntryCount)
    307    : mManager(aManager),
    308      mOpResult(aOpResult),
    309      mStreamControl(nullptr),
    310      mSent(false) {
    311  MOZ_RELEASE_ASSERT(aEntryCount != 0);
    312  if (mOpResult.type() == CacheOpResult::TCacheMatchAllResult) {
    313    CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
    314    result.responseList().SetCapacity(aEntryCount);
    315  } else if (mOpResult.type() == CacheOpResult::TCacheKeysResult) {
    316    CacheKeysResult& result = mOpResult.get_CacheKeysResult();
    317    result.requestList().SetCapacity(aEntryCount);
    318  } else {
    319    MOZ_DIAGNOSTIC_ASSERT(aEntryCount == 1);
    320  }
    321 }
    322 
    323 AutoParentOpResult::~AutoParentOpResult() {
    324  CleanupAction action = mSent ? Forget : Delete;
    325 
    326  switch (mOpResult.type()) {
    327    case CacheOpResult::TStorageOpenResult: {
    328      StorageOpenResult& result = mOpResult.get_StorageOpenResult();
    329      if (action == Forget || !result.actor()) {
    330        break;
    331      }
    332 
    333      QM_WARNONLY_TRY(
    334          OkIf(PCacheParent::Send__delete__(result.actor().AsParent())));
    335      break;
    336    }
    337    default:
    338      // other types do not need additional clean up
    339      break;
    340  }
    341 
    342  if (action == Delete && mStreamControl) {
    343    mStreamControl->AssertWillDelete();
    344    QM_WARNONLY_TRY(
    345        OkIf(PCacheStreamControlParent::Send__delete__(mStreamControl)));
    346  }
    347 }
    348 
    349 void AutoParentOpResult::Add(CacheId aOpenedCacheId,
    350                             SafeRefPtr<Manager> aManager) {
    351  MOZ_DIAGNOSTIC_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
    352  MOZ_DIAGNOSTIC_ASSERT(!mOpResult.get_StorageOpenResult().actor());
    353 
    354  PCacheParent* cacheParent = nullptr;
    355  if (mManager.is<pPBoundStorageKeyParent>()) {
    356    auto* manager = mManager.as<pPBoundStorageKeyParent>();
    357    MOZ_ASSERT(manager);
    358 
    359    cacheParent = manager->SendPCacheConstructor(
    360        new CacheParent(mManager, std::move(aManager), aOpenedCacheId));
    361  } else {
    362    MOZ_ASSERT(mManager.is<pPBackgroundParent>());
    363    auto* manager = mManager.as<pPBackgroundParent>();
    364    MOZ_ASSERT(manager);
    365 
    366    cacheParent = manager->SendPCacheConstructor(
    367        new CacheParent(mManager, std::move(aManager), aOpenedCacheId));
    368  }
    369 
    370  mOpResult.get_StorageOpenResult().actor() = cacheParent;
    371 }
    372 
    373 void AutoParentOpResult::Add(const SavedResponse& aSavedResponse,
    374                             StreamList& aStreamList) {
    375  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    376 
    377  switch (mOpResult.type()) {
    378    case CacheOpResult::TCacheMatchResult: {
    379      CacheMatchResult& result = mOpResult.get_CacheMatchResult();
    380      MOZ_DIAGNOSTIC_ASSERT(result.maybeResponse().isNothing());
    381      result.maybeResponse().emplace(aSavedResponse.mValue);
    382      SerializeResponseBody(aSavedResponse, aStreamList,
    383                            &result.maybeResponse().ref());
    384      break;
    385    }
    386    case CacheOpResult::TCacheMatchAllResult: {
    387      CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
    388      MOZ_RELEASE_ASSERT(result.responseList().Length() <
    389                         result.responseList().Capacity());
    390      result.responseList().AppendElement(aSavedResponse.mValue);
    391      SerializeResponseBody(aSavedResponse, aStreamList,
    392                            &result.responseList().LastElement());
    393      break;
    394    }
    395    case CacheOpResult::TStorageMatchResult: {
    396      StorageMatchResult& result = mOpResult.get_StorageMatchResult();
    397      MOZ_DIAGNOSTIC_ASSERT(result.maybeResponse().isNothing());
    398      result.maybeResponse().emplace(aSavedResponse.mValue);
    399      SerializeResponseBody(aSavedResponse, aStreamList,
    400                            &result.maybeResponse().ref());
    401      break;
    402    }
    403    default:
    404      MOZ_CRASH("Cache result type cannot handle returning a Response!");
    405  }
    406 }
    407 
    408 void AutoParentOpResult::Add(const SavedRequest& aSavedRequest,
    409                             StreamList& aStreamList) {
    410  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    411 
    412  switch (mOpResult.type()) {
    413    case CacheOpResult::TCacheKeysResult: {
    414      CacheKeysResult& result = mOpResult.get_CacheKeysResult();
    415      MOZ_RELEASE_ASSERT(result.requestList().Length() <
    416                         result.requestList().Capacity());
    417      result.requestList().AppendElement(aSavedRequest.mValue);
    418      CacheRequest& request = result.requestList().LastElement();
    419 
    420      if (!aSavedRequest.mHasBodyId) {
    421        request.body() = Nothing();
    422        break;
    423      }
    424 
    425      request.body().emplace(CacheReadStream());
    426      SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
    427                          &request.body().ref());
    428      break;
    429    }
    430    default:
    431      MOZ_CRASH("Cache result type cannot handle returning a Request!");
    432  }
    433 }
    434 
    435 const CacheOpResult& AutoParentOpResult::SendAsOpResult() {
    436  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    437  mSent = true;
    438  return mOpResult;
    439 }
    440 
    441 void AutoParentOpResult::SerializeResponseBody(
    442    const SavedResponse& aSavedResponse, StreamList& aStreamList,
    443    CacheResponse* aResponseOut) {
    444  MOZ_DIAGNOSTIC_ASSERT(aResponseOut);
    445 
    446  if (!aSavedResponse.mHasBodyId) {
    447    aResponseOut->body() = Nothing();
    448    return;
    449  }
    450 
    451  aResponseOut->body().emplace(CacheReadStream());
    452  SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
    453                      &aResponseOut->body().ref());
    454 }
    455 
    456 void AutoParentOpResult::SerializeReadStream(const nsID& aId,
    457                                             StreamList& aStreamList,
    458                                             CacheReadStream* aReadStreamOut) {
    459  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
    460  MOZ_DIAGNOSTIC_ASSERT(!mSent);
    461 
    462  nsCOMPtr<nsIInputStream> stream = aStreamList.Extract(aId);
    463 
    464  CacheStreamControlParent* cacheStreamControlParent = nullptr;
    465  if (!mStreamControl) {
    466    if (mManager.is<pPBoundStorageKeyParent>()) {
    467      auto* manager = mManager.as<pPBoundStorageKeyParent>();
    468      cacheStreamControlParent = static_cast<CacheStreamControlParent*>(
    469          manager->SendPCacheStreamControlConstructor(
    470              new CacheStreamControlParent()));
    471    } else {
    472      MOZ_ASSERT(mManager.is<pPBackgroundParent>());
    473 
    474      auto* manager = mManager.as<pPBackgroundParent>();
    475      cacheStreamControlParent = static_cast<CacheStreamControlParent*>(
    476          manager->SendPCacheStreamControlConstructor(
    477              new CacheStreamControlParent()));
    478    }
    479    mStreamControl = cacheStreamControlParent;
    480 
    481    // If this failed, then the child process is gone.  Warn and allow actor
    482    // cleanup to proceed as normal.
    483    if (!mStreamControl) {
    484      NS_WARNING("Cache failed to create stream control actor.");
    485      return;
    486    }
    487  }
    488 
    489  aStreamList.SetStreamControl(mStreamControl);
    490 
    491  RefPtr<ReadStream> readStream =
    492      ReadStream::Create(mStreamControl, aId, stream);
    493  ErrorResult rv;
    494  readStream->Serialize(aReadStreamOut, rv);
    495  MOZ_DIAGNOSTIC_ASSERT(!rv.Failed());
    496 }
    497 
    498 }  // namespace mozilla::dom::cache