tor-browser

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

nsMIMEInputStream.cpp (15597B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /**
      7 * The MIME stream separates headers and a datastream. It also allows
      8 * automatic creation of the content-length header.
      9 */
     10 
     11 #include "nsMIMEInputStream.h"
     12 
     13 #include "ipc/IPCMessageUtils.h"
     14 #include "mozilla/Mutex.h"
     15 #include "mozilla/ipc/InputStreamUtils.h"
     16 #include "nsCOMPtr.h"
     17 #include "nsComponentManagerUtils.h"
     18 #include "nsIAsyncInputStream.h"
     19 #include "nsIClassInfoImpl.h"
     20 #include "nsIHttpHeaderVisitor.h"
     21 #include "nsIIPCSerializableInputStream.h"
     22 #include "nsIInputStreamLength.h"
     23 #include "nsIMIMEInputStream.h"
     24 #include "nsISeekableStream.h"
     25 #include "nsString.h"
     26 
     27 using namespace mozilla::ipc;
     28 using mozilla::Maybe;
     29 
     30 class nsMIMEInputStream : public nsIMIMEInputStream,
     31                          public nsISeekableStream,
     32                          public nsIIPCSerializableInputStream,
     33                          public nsIAsyncInputStream,
     34                          public nsIInputStreamCallback,
     35                          public nsIInputStreamLength,
     36                          public nsIAsyncInputStreamLength,
     37                          public nsIInputStreamLengthCallback,
     38                          public nsICloneableInputStream {
     39  virtual ~nsMIMEInputStream() = default;
     40 
     41 public:
     42  nsMIMEInputStream() = default;
     43 
     44  NS_DECL_THREADSAFE_ISUPPORTS
     45  NS_DECL_NSIINPUTSTREAM
     46  NS_DECL_NSIMIMEINPUTSTREAM
     47  NS_DECL_NSISEEKABLESTREAM
     48  NS_DECL_NSITELLABLESTREAM
     49  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
     50  NS_DECL_NSIASYNCINPUTSTREAM
     51  NS_DECL_NSIINPUTSTREAMCALLBACK
     52  NS_DECL_NSIINPUTSTREAMLENGTH
     53  NS_DECL_NSIASYNCINPUTSTREAMLENGTH
     54  NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
     55  NS_DECL_NSICLONEABLEINPUTSTREAM
     56 
     57 private:
     58  struct MOZ_STACK_CLASS ReadSegmentsState {
     59    nsCOMPtr<nsIInputStream> mThisStream;
     60    nsWriteSegmentFun mWriter{nullptr};
     61    void* mClosure{nullptr};
     62  };
     63  static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
     64                            const char* aFromRawSegment, uint32_t aToOffset,
     65                            uint32_t aCount, uint32_t* aWriteCount);
     66 
     67  bool IsSeekableInputStream() const;
     68  bool IsAsyncInputStream() const;
     69  bool IsInputStreamLength() const;
     70  bool IsAsyncInputStreamLength() const;
     71  bool IsCloneableInputStream() const;
     72 
     73  nsTArray<HeaderEntry> mHeaders;
     74 
     75  nsCOMPtr<nsIInputStream> mStream;
     76  mozilla::Atomic<bool, mozilla::Relaxed> mStartedReading{false};
     77 
     78  mozilla::Mutex mMutex{"nsMIMEInputStream::mMutex"};
     79  nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback MOZ_GUARDED_BY(mMutex);
     80 
     81  // This is protected by mutex.
     82  nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback;
     83 };
     84 
     85 NS_IMPL_ADDREF(nsMIMEInputStream)
     86 NS_IMPL_RELEASE(nsMIMEInputStream)
     87 
     88 NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
     89                  NS_MIMEINPUTSTREAM_CID)
     90 
     91 NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream)
     92  NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream)
     93  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMIMEInputStream)
     94  NS_INTERFACE_MAP_ENTRY(nsITellableStream)
     95  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableInputStream())
     96  NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
     97  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream())
     98  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
     99                                     IsAsyncInputStream())
    100  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
    101  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
    102                                     IsInputStreamLength())
    103  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
    104                                     IsAsyncInputStreamLength())
    105  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback,
    106                                     IsAsyncInputStreamLength())
    107  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
    108                                     IsCloneableInputStream())
    109  NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
    110 NS_INTERFACE_MAP_END
    111 
    112 NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream, nsIMIMEInputStream,
    113                            nsIAsyncInputStream, nsIInputStream,
    114                            nsISeekableStream, nsITellableStream)
    115 
    116 NS_IMETHODIMP
    117 nsMIMEInputStream::AddHeader(const char* aName, const char* aValue) {
    118  NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
    119 
    120  HeaderEntry* entry = mHeaders.AppendElement();
    121  entry->name().Append(aName);
    122  entry->value().Append(aValue);
    123 
    124  return NS_OK;
    125 }
    126 
    127 NS_IMETHODIMP
    128 nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor* visitor) {
    129  nsresult rv;
    130 
    131  for (auto& header : mHeaders) {
    132    rv = visitor->VisitHeader(header.name(), header.value());
    133    if (NS_FAILED(rv)) {
    134      return rv;
    135    }
    136  }
    137  return NS_OK;
    138 }
    139 
    140 NS_IMETHODIMP
    141 nsMIMEInputStream::SetData(nsIInputStream* aStream) {
    142  NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
    143 
    144  mStream = aStream;
    145  return NS_OK;
    146 }
    147 
    148 NS_IMETHODIMP
    149 nsMIMEInputStream::GetData(nsIInputStream** aStream) {
    150  NS_ENSURE_ARG_POINTER(aStream);
    151  *aStream = do_AddRef(mStream).take();
    152  return NS_OK;
    153 }
    154 
    155 #define INITSTREAMS                               \
    156  if (!mStartedReading) {                         \
    157    NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \
    158    mStartedReading = true;                       \
    159  }
    160 
    161 // Reset mStartedReading when Seek-ing to start
    162 NS_IMETHODIMP
    163 nsMIMEInputStream::Seek(int32_t whence, int64_t offset) {
    164  NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED);
    165 
    166  nsresult rv;
    167  nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
    168 
    169  if (whence == NS_SEEK_SET && offset == 0) {
    170    rv = stream->Seek(whence, offset);
    171    if (NS_SUCCEEDED(rv)) mStartedReading = false;
    172  } else {
    173    INITSTREAMS;
    174    rv = stream->Seek(whence, offset);
    175  }
    176 
    177  return rv;
    178 }
    179 
    180 // Proxy ReadSegments since we need to be a good little nsIInputStream
    181 NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
    182                                              void* aClosure, uint32_t aCount,
    183                                              uint32_t* _retval) {
    184  INITSTREAMS;
    185  ReadSegmentsState state;
    186  // Disambiguate ambiguous nsIInputStream.
    187  state.mThisStream =
    188      static_cast<nsIInputStream*>(static_cast<nsIMIMEInputStream*>(this));
    189  state.mWriter = aWriter;
    190  state.mClosure = aClosure;
    191  return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
    192 }
    193 
    194 nsresult nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
    195                                      const char* aFromRawSegment,
    196                                      uint32_t aToOffset, uint32_t aCount,
    197                                      uint32_t* aWriteCount) {
    198  ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
    199  return (state->mWriter)(state->mThisStream, state->mClosure, aFromRawSegment,
    200                          aToOffset, aCount, aWriteCount);
    201 }
    202 
    203 /**
    204 * Forward everything else to the mStream after calling INITSTREAMS
    205 */
    206 
    207 // nsIInputStream
    208 NS_IMETHODIMP nsMIMEInputStream::Close(void) {
    209  INITSTREAMS;
    210  return mStream->Close();
    211 }
    212 NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t* _retval) {
    213  INITSTREAMS;
    214  return mStream->Available(_retval);
    215 }
    216 NS_IMETHODIMP nsMIMEInputStream::StreamStatus() {
    217  INITSTREAMS;
    218  return mStream->StreamStatus();
    219 }
    220 NS_IMETHODIMP nsMIMEInputStream::Read(char* buf, uint32_t count,
    221                                      uint32_t* _retval) {
    222  INITSTREAMS;
    223  return mStream->Read(buf, count, _retval);
    224 }
    225 NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool* aNonBlocking) {
    226  INITSTREAMS;
    227  return mStream->IsNonBlocking(aNonBlocking);
    228 }
    229 
    230 // nsIAsyncInputStream
    231 NS_IMETHODIMP
    232 nsMIMEInputStream::CloseWithStatus(nsresult aStatus) {
    233  INITSTREAMS;
    234  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
    235  return asyncStream->CloseWithStatus(aStatus);
    236 }
    237 
    238 NS_IMETHODIMP
    239 nsMIMEInputStream::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
    240                             uint32_t aRequestedCount,
    241                             nsIEventTarget* aEventTarget) {
    242  INITSTREAMS;
    243  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
    244  if (NS_WARN_IF(!asyncStream)) {
    245    return NS_ERROR_FAILURE;
    246  }
    247 
    248  nsCOMPtr<nsIInputStreamCallback> callback = aCallback ? this : nullptr;
    249  {
    250    mozilla::MutexAutoLock lock(mMutex);
    251    if (NS_WARN_IF(mAsyncWaitCallback && aCallback &&
    252                   mAsyncWaitCallback != aCallback)) {
    253      return NS_ERROR_FAILURE;
    254    }
    255 
    256    mAsyncWaitCallback = aCallback;
    257  }
    258 
    259  return asyncStream->AsyncWait(callback, aFlags, aRequestedCount,
    260                                aEventTarget);
    261 }
    262 
    263 // nsIInputStreamCallback
    264 
    265 NS_IMETHODIMP
    266 nsMIMEInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
    267  nsCOMPtr<nsIInputStreamCallback> callback;
    268 
    269  {
    270    mozilla::MutexAutoLock lock(mMutex);
    271 
    272    // We have been canceled in the meanwhile.
    273    if (!mAsyncWaitCallback) {
    274      return NS_OK;
    275    }
    276 
    277    callback.swap(mAsyncWaitCallback);
    278  }
    279 
    280  MOZ_ASSERT(callback);
    281  return callback->OnInputStreamReady(this);
    282 }
    283 
    284 // nsITellableStream
    285 NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t* _retval) {
    286  INITSTREAMS;
    287  nsCOMPtr<nsITellableStream> stream = do_QueryInterface(mStream);
    288  return stream->Tell(_retval);
    289 }
    290 
    291 // nsISeekableStream
    292 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
    293  INITSTREAMS;
    294  nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
    295  return stream->SetEOF();
    296 }
    297 
    298 /**
    299 * Factory method used by do_CreateInstance
    300 */
    301 
    302 nsresult nsMIMEInputStreamConstructor(REFNSIID iid, void** result) {
    303  *result = nullptr;
    304 
    305  RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
    306  if (!inst) return NS_ERROR_OUT_OF_MEMORY;
    307 
    308  return inst->QueryInterface(iid, result);
    309 }
    310 
    311 void nsMIMEInputStream::SerializedComplexity(uint32_t aMaxSize,
    312                                             uint32_t* aSizeUsed,
    313                                             uint32_t* aPipes,
    314                                             uint32_t* aTransferables) {
    315  if (nsCOMPtr<nsIIPCSerializableInputStream> serializable =
    316          do_QueryInterface(mStream)) {
    317    InputStreamHelper::SerializedComplexity(mStream, aMaxSize, aSizeUsed,
    318                                            aPipes, aTransferables);
    319  } else {
    320    *aPipes = 1;
    321  }
    322 }
    323 
    324 void nsMIMEInputStream::Serialize(InputStreamParams& aParams, uint32_t aMaxSize,
    325                                  uint32_t* aSizeUsed) {
    326  MOZ_ASSERT(aSizeUsed);
    327  *aSizeUsed = 0;
    328 
    329  MIMEInputStreamParams params;
    330  params.headers() = mHeaders.Clone();
    331  params.startedReading() = mStartedReading;
    332 
    333  if (!mStream) {
    334    aParams = params;
    335    return;
    336  }
    337 
    338  InputStreamParams wrappedParams;
    339 
    340  if (nsCOMPtr<nsIIPCSerializableInputStream> serializable =
    341          do_QueryInterface(mStream)) {
    342    InputStreamHelper::SerializeInputStream(mStream, wrappedParams, aMaxSize,
    343                                            aSizeUsed);
    344  } else {
    345    // Falling back to sending the underlying stream over a pipe when
    346    // sending an nsMIMEInputStream over IPC is potentially wasteful
    347    // if it is sent several times. This can possibly happen with
    348    // fission. There are two ways to improve this, see bug 1648369
    349    // and bug 1648370.
    350    InputStreamHelper::SerializeInputStreamAsPipe(mStream, wrappedParams);
    351  }
    352 
    353  NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
    354               "Wrapped stream failed to serialize!");
    355 
    356  params.optionalStream().emplace(wrappedParams);
    357  aParams = params;
    358 }
    359 
    360 bool nsMIMEInputStream::Deserialize(const InputStreamParams& aParams) {
    361  if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
    362    NS_ERROR("Received unknown parameters from the other process!");
    363    return false;
    364  }
    365 
    366  const MIMEInputStreamParams& params = aParams.get_MIMEInputStreamParams();
    367  const Maybe<InputStreamParams>& wrappedParams = params.optionalStream();
    368 
    369  if (wrappedParams.isSome()) {
    370    nsCOMPtr<nsIInputStream> stream;
    371    stream = InputStreamHelper::DeserializeInputStream(wrappedParams.ref());
    372    if (!stream) {
    373      NS_WARNING("Failed to deserialize wrapped stream!");
    374      return false;
    375    }
    376 
    377    MOZ_ALWAYS_SUCCEEDS(SetData(stream));
    378  }
    379 
    380  mHeaders = params.headers().Clone();
    381  mStartedReading = params.startedReading();
    382 
    383  return true;
    384 }
    385 
    386 NS_IMETHODIMP
    387 nsMIMEInputStream::Length(int64_t* aLength) {
    388  nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
    389  if (NS_WARN_IF(!stream)) {
    390    return NS_ERROR_FAILURE;
    391  }
    392 
    393  return stream->Length(aLength);
    394 }
    395 
    396 NS_IMETHODIMP
    397 nsMIMEInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
    398                                   nsIEventTarget* aEventTarget) {
    399  nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
    400  if (NS_WARN_IF(!stream)) {
    401    return NS_ERROR_FAILURE;
    402  }
    403 
    404  nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr;
    405  {
    406    mozilla::MutexAutoLock lock(mMutex);
    407    mAsyncInputStreamLengthCallback = aCallback;
    408  }
    409 
    410  return stream->AsyncLengthWait(callback, aEventTarget);
    411 }
    412 
    413 NS_IMETHODIMP
    414 nsMIMEInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
    415                                            int64_t aLength) {
    416  nsCOMPtr<nsIInputStreamLengthCallback> callback;
    417  {
    418    mozilla::MutexAutoLock lock(mMutex);
    419    // We have been canceled in the meanwhile.
    420    if (!mAsyncInputStreamLengthCallback) {
    421      return NS_OK;
    422    }
    423 
    424    callback.swap(mAsyncInputStreamLengthCallback);
    425  }
    426 
    427  MOZ_ASSERT(callback);
    428  return callback->OnInputStreamLengthReady(this, aLength);
    429 }
    430 
    431 bool nsMIMEInputStream::IsSeekableInputStream() const {
    432  nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
    433  return !!seekable;
    434 }
    435 
    436 bool nsMIMEInputStream::IsAsyncInputStream() const {
    437  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
    438  return !!asyncStream;
    439 }
    440 
    441 bool nsMIMEInputStream::IsInputStreamLength() const {
    442  nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
    443  return !!stream;
    444 }
    445 
    446 bool nsMIMEInputStream::IsAsyncInputStreamLength() const {
    447  nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
    448  return !!stream;
    449 }
    450 
    451 bool nsMIMEInputStream::IsCloneableInputStream() const {
    452  nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
    453  return !!stream;
    454 }
    455 
    456 // nsICloneableInputStream interface
    457 
    458 NS_IMETHODIMP
    459 nsMIMEInputStream::GetCloneable(bool* aCloneable) {
    460  nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
    461  if (!mStream) {
    462    return NS_ERROR_FAILURE;
    463  }
    464 
    465  return stream->GetCloneable(aCloneable);
    466 }
    467 
    468 NS_IMETHODIMP
    469 nsMIMEInputStream::Clone(nsIInputStream** aResult) {
    470  nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
    471  if (!mStream) {
    472    return NS_ERROR_FAILURE;
    473  }
    474 
    475  nsCOMPtr<nsIInputStream> clonedStream;
    476  nsresult rv = stream->Clone(getter_AddRefs(clonedStream));
    477  if (NS_WARN_IF(NS_FAILED(rv))) {
    478    return rv;
    479  }
    480 
    481  nsCOMPtr<nsIMIMEInputStream> mimeStream = new nsMIMEInputStream();
    482 
    483  rv = mimeStream->SetData(clonedStream);
    484  if (NS_WARN_IF(NS_FAILED(rv))) {
    485    return rv;
    486  }
    487 
    488  for (const HeaderEntry& entry : mHeaders) {
    489    rv = mimeStream->AddHeader(entry.name().get(), entry.value().get());
    490    MOZ_ASSERT(NS_SUCCEEDED(rv));
    491  }
    492 
    493  static_cast<nsMIMEInputStream*>(mimeStream.get())->mStartedReading =
    494      static_cast<bool>(mStartedReading);
    495 
    496  mimeStream.forget(aResult);
    497  return NS_OK;
    498 }