tor-browser

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

nsSyncStreamListener.cpp (5176B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "mozilla/SpinEventLoopUntil.h"
      6 #include "nsIOService.h"
      7 #include "nsIPipe.h"
      8 #include "nsSyncStreamListener.h"
      9 #include "nsThreadUtils.h"
     10 #include <algorithm>
     11 
     12 using namespace mozilla::net;
     13 
     14 nsSyncStreamListener::nsSyncStreamListener() {
     15  MOZ_ASSERT(NS_IsMainThread());
     16  NS_NewPipe(getter_AddRefs(mPipeIn), getter_AddRefs(mPipeOut),
     17             mozilla::net::nsIOService::gDefaultSegmentSize,
     18             UINT32_MAX,  // no size limit
     19             false, false);
     20 }
     21 
     22 nsresult nsSyncStreamListener::WaitForData() {
     23  mKeepWaiting = true;
     24 
     25  if (!mozilla::SpinEventLoopUntil("nsSyncStreamListener::Create"_ns,
     26                                   [&]() { return !mKeepWaiting; })) {
     27    return NS_ERROR_FAILURE;
     28  }
     29 
     30  return NS_OK;
     31 }
     32 
     33 //-----------------------------------------------------------------------------
     34 // nsSyncStreamListener::nsISupports
     35 //-----------------------------------------------------------------------------
     36 
     37 NS_IMPL_ISUPPORTS(nsSyncStreamListener, nsIStreamListener, nsIRequestObserver,
     38                  nsIInputStream, nsISyncStreamListener)
     39 
     40 //-----------------------------------------------------------------------------
     41 // nsSyncStreamListener::nsISyncStreamListener
     42 //-----------------------------------------------------------------------------
     43 
     44 NS_IMETHODIMP
     45 nsSyncStreamListener::GetInputStream(nsIInputStream** result) {
     46  *result = do_AddRef(this).take();
     47  return NS_OK;
     48 }
     49 
     50 //-----------------------------------------------------------------------------
     51 // nsSyncStreamListener::nsIStreamListener
     52 //-----------------------------------------------------------------------------
     53 
     54 NS_IMETHODIMP
     55 nsSyncStreamListener::OnStartRequest(nsIRequest* request) { return NS_OK; }
     56 
     57 NS_IMETHODIMP
     58 nsSyncStreamListener::OnDataAvailable(nsIRequest* request,
     59                                      nsIInputStream* stream, uint64_t offset,
     60                                      uint32_t count) {
     61  uint32_t bytesWritten;
     62 
     63  nsresult rv = mPipeOut->WriteFrom(stream, count, &bytesWritten);
     64 
     65  // if we get an error, then return failure.  this will cause the
     66  // channel to be canceled, and as a result our OnStopRequest method
     67  // will be called immediately.  because of this we do not need to
     68  // set mStatus or mKeepWaiting here.
     69  if (NS_FAILED(rv)) return rv;
     70 
     71  // we expect that all data will be written to the pipe because
     72  // the pipe was created to have "infinite" room.
     73  NS_ASSERTION(bytesWritten == count, "did not write all data");
     74 
     75  mKeepWaiting = false;  // unblock Read
     76  return NS_OK;
     77 }
     78 
     79 NS_IMETHODIMP
     80 nsSyncStreamListener::OnStopRequest(nsIRequest* request, nsresult status) {
     81  mStatus = status;
     82  mKeepWaiting = false;  // unblock Read
     83  mDone = true;
     84  return NS_OK;
     85 }
     86 
     87 //-----------------------------------------------------------------------------
     88 // nsSyncStreamListener::nsIInputStream
     89 //-----------------------------------------------------------------------------
     90 
     91 NS_IMETHODIMP
     92 nsSyncStreamListener::Close() {
     93  mStatus = NS_BASE_STREAM_CLOSED;
     94  mDone = true;
     95 
     96  // It'd be nice if we could explicitly cancel the request at this point,
     97  // but we don't have a reference to it, so the best we can do is close the
     98  // pipe so that the next OnDataAvailable event will fail.
     99  if (mPipeIn) {
    100    mPipeIn->Close();
    101    mPipeIn = nullptr;
    102  }
    103  return NS_OK;
    104 }
    105 
    106 NS_IMETHODIMP
    107 nsSyncStreamListener::Available(uint64_t* result) {
    108  if (NS_FAILED(mStatus)) return mStatus;
    109 
    110  mStatus = mPipeIn->Available(result);
    111  if (NS_SUCCEEDED(mStatus) && (*result == 0) && !mDone) {
    112    nsresult rv = WaitForData();
    113    if (NS_FAILED(rv)) {
    114      // Note that `WaitForData` could fail `mStatus`. Do not overwrite if it's
    115      // the case.
    116      mStatus = NS_SUCCEEDED(mStatus) ? rv : mStatus;
    117    } else if (NS_SUCCEEDED(mStatus)) {
    118      mStatus = mPipeIn->Available(result);
    119    }
    120  }
    121  return mStatus;
    122 }
    123 
    124 NS_IMETHODIMP
    125 nsSyncStreamListener::StreamStatus() {
    126  if (NS_FAILED(mStatus)) {
    127    return mStatus;
    128  }
    129 
    130  mStatus = mPipeIn->StreamStatus();
    131  return mStatus;
    132 }
    133 
    134 NS_IMETHODIMP
    135 nsSyncStreamListener::Read(char* buf, uint32_t bufLen, uint32_t* result) {
    136  if (mStatus == NS_BASE_STREAM_CLOSED) {
    137    *result = 0;
    138    return NS_OK;
    139  }
    140 
    141  uint64_t avail64;
    142  if (NS_FAILED(Available(&avail64))) return mStatus;
    143 
    144  uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)bufLen);
    145  mStatus = mPipeIn->Read(buf, avail, result);
    146  return mStatus;
    147 }
    148 
    149 NS_IMETHODIMP
    150 nsSyncStreamListener::ReadSegments(nsWriteSegmentFun writer, void* closure,
    151                                   uint32_t count, uint32_t* result) {
    152  if (mStatus == NS_BASE_STREAM_CLOSED) {
    153    *result = 0;
    154    return NS_OK;
    155  }
    156 
    157  uint64_t avail64;
    158  if (NS_FAILED(Available(&avail64))) return mStatus;
    159 
    160  uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)count);
    161  mStatus = mPipeIn->ReadSegments(writer, closure, avail, result);
    162  return mStatus;
    163 }
    164 
    165 NS_IMETHODIMP
    166 nsSyncStreamListener::IsNonBlocking(bool* result) {
    167  *result = false;
    168  return NS_OK;
    169 }