xpcom/io/nsInputStreamTee.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include <stdlib.h>
michael@0 7 #include "prlog.h"
michael@0 8
michael@0 9 #include "mozilla/Mutex.h"
michael@0 10 #include "mozilla/Attributes.h"
michael@0 11 #include "nsIInputStreamTee.h"
michael@0 12 #include "nsIInputStream.h"
michael@0 13 #include "nsIOutputStream.h"
michael@0 14 #include "nsCOMPtr.h"
michael@0 15 #include "nsAutoPtr.h"
michael@0 16 #include "nsIEventTarget.h"
michael@0 17 #include "nsThreadUtils.h"
michael@0 18
michael@0 19 using namespace mozilla;
michael@0 20
michael@0 21 #ifdef LOG
michael@0 22 #undef LOG
michael@0 23 #endif
michael@0 24 #ifdef PR_LOGGING
michael@0 25 static PRLogModuleInfo*
michael@0 26 GetTeeLog()
michael@0 27 {
michael@0 28 static PRLogModuleInfo *sLog;
michael@0 29 if (!sLog)
michael@0 30 sLog = PR_NewLogModule("nsInputStreamTee");
michael@0 31 return sLog;
michael@0 32 }
michael@0 33 #define LOG(args) PR_LOG(GetTeeLog(), PR_LOG_DEBUG, args)
michael@0 34 #else
michael@0 35 #define LOG(args)
michael@0 36 #endif
michael@0 37
michael@0 38 class nsInputStreamTee MOZ_FINAL : public nsIInputStreamTee
michael@0 39 {
michael@0 40 public:
michael@0 41 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 42 NS_DECL_NSIINPUTSTREAM
michael@0 43 NS_DECL_NSIINPUTSTREAMTEE
michael@0 44
michael@0 45 nsInputStreamTee();
michael@0 46 bool SinkIsValid();
michael@0 47 void InvalidateSink();
michael@0 48
michael@0 49 private:
michael@0 50 ~nsInputStreamTee() {}
michael@0 51
michael@0 52 nsresult TeeSegment(const char *buf, uint32_t count);
michael@0 53
michael@0 54 static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
michael@0 55 uint32_t, uint32_t, uint32_t *);
michael@0 56
michael@0 57 private:
michael@0 58 nsCOMPtr<nsIInputStream> mSource;
michael@0 59 nsCOMPtr<nsIOutputStream> mSink;
michael@0 60 nsCOMPtr<nsIEventTarget> mEventTarget;
michael@0 61 nsWriteSegmentFun mWriter; // for implementing ReadSegments
michael@0 62 void *mClosure; // for implementing ReadSegments
michael@0 63 nsAutoPtr<Mutex> mLock; // synchronize access to mSinkIsValid
michael@0 64 bool mSinkIsValid; // False if TeeWriteEvent fails
michael@0 65 };
michael@0 66
michael@0 67 class nsInputStreamTeeWriteEvent : public nsRunnable {
michael@0 68 public:
michael@0 69 // aTee's lock is held across construction of this object
michael@0 70 nsInputStreamTeeWriteEvent(const char *aBuf, uint32_t aCount,
michael@0 71 nsIOutputStream *aSink,
michael@0 72 nsInputStreamTee *aTee)
michael@0 73 {
michael@0 74 // copy the buffer - will be free'd by dtor
michael@0 75 mBuf = (char *)malloc(aCount);
michael@0 76 if (mBuf) memcpy(mBuf, (char *)aBuf, aCount);
michael@0 77 mCount = aCount;
michael@0 78 mSink = aSink;
michael@0 79 bool isNonBlocking;
michael@0 80 mSink->IsNonBlocking(&isNonBlocking);
michael@0 81 NS_ASSERTION(isNonBlocking == false, "mSink is nonblocking");
michael@0 82 mTee = aTee;
michael@0 83 }
michael@0 84
michael@0 85 NS_IMETHOD Run()
michael@0 86 {
michael@0 87 if (!mBuf) {
michael@0 88 NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
michael@0 89 "memory not allocated\n");
michael@0 90 return NS_OK;
michael@0 91 }
michael@0 92 NS_ABORT_IF_FALSE(mSink, "mSink is null!");
michael@0 93
michael@0 94 // The output stream could have been invalidated between when
michael@0 95 // this event was dispatched and now, so check before writing.
michael@0 96 if (!mTee->SinkIsValid()) {
michael@0 97 return NS_OK;
michael@0 98 }
michael@0 99
michael@0 100 LOG(("nsInputStreamTeeWriteEvent::Run() [%p]"
michael@0 101 "will write %u bytes to %p\n",
michael@0 102 this, mCount, mSink.get()));
michael@0 103
michael@0 104 uint32_t totalBytesWritten = 0;
michael@0 105 while (mCount) {
michael@0 106 nsresult rv;
michael@0 107 uint32_t bytesWritten = 0;
michael@0 108 rv = mSink->Write(mBuf + totalBytesWritten, mCount, &bytesWritten);
michael@0 109 if (NS_FAILED(rv)) {
michael@0 110 LOG(("nsInputStreamTeeWriteEvent::Run[%p] error %x in writing",
michael@0 111 this,rv));
michael@0 112 mTee->InvalidateSink();
michael@0 113 break;
michael@0 114 }
michael@0 115 totalBytesWritten += bytesWritten;
michael@0 116 NS_ASSERTION(bytesWritten <= mCount, "wrote too much");
michael@0 117 mCount -= bytesWritten;
michael@0 118 }
michael@0 119 return NS_OK;
michael@0 120 }
michael@0 121
michael@0 122 protected:
michael@0 123 virtual ~nsInputStreamTeeWriteEvent()
michael@0 124 {
michael@0 125 if (mBuf) free(mBuf);
michael@0 126 mBuf = nullptr;
michael@0 127 }
michael@0 128
michael@0 129 private:
michael@0 130 char *mBuf;
michael@0 131 uint32_t mCount;
michael@0 132 nsCOMPtr<nsIOutputStream> mSink;
michael@0 133 // back pointer to the tee that created this runnable
michael@0 134 nsRefPtr<nsInputStreamTee> mTee;
michael@0 135 };
michael@0 136
michael@0 137 nsInputStreamTee::nsInputStreamTee(): mLock(nullptr)
michael@0 138 , mSinkIsValid(true)
michael@0 139 {
michael@0 140 }
michael@0 141
michael@0 142 bool
michael@0 143 nsInputStreamTee::SinkIsValid()
michael@0 144 {
michael@0 145 MutexAutoLock lock(*mLock);
michael@0 146 return mSinkIsValid;
michael@0 147 }
michael@0 148
michael@0 149 void
michael@0 150 nsInputStreamTee::InvalidateSink()
michael@0 151 {
michael@0 152 MutexAutoLock lock(*mLock);
michael@0 153 mSinkIsValid = false;
michael@0 154 }
michael@0 155
michael@0 156 nsresult
michael@0 157 nsInputStreamTee::TeeSegment(const char *buf, uint32_t count)
michael@0 158 {
michael@0 159 if (!mSink) return NS_OK; // nothing to do
michael@0 160 if (mLock) { // asynchronous case
michael@0 161 NS_ASSERTION(mEventTarget, "mEventTarget is null, mLock is not null.");
michael@0 162 if (!SinkIsValid()) {
michael@0 163 return NS_OK; // nothing to do
michael@0 164 }
michael@0 165 nsRefPtr<nsIRunnable> event =
michael@0 166 new nsInputStreamTeeWriteEvent(buf, count, mSink, this);
michael@0 167 LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
michael@0 168 this, count));
michael@0 169 return mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 170 } else { // synchronous case
michael@0 171 NS_ASSERTION(!mEventTarget, "mEventTarget is not null, mLock is null.");
michael@0 172 nsresult rv;
michael@0 173 uint32_t totalBytesWritten = 0;
michael@0 174 while (count) {
michael@0 175 uint32_t bytesWritten = 0;
michael@0 176 rv = mSink->Write(buf + totalBytesWritten, count, &bytesWritten);
michael@0 177 if (NS_FAILED(rv)) {
michael@0 178 // ok, this is not a fatal error... just drop our reference to mSink
michael@0 179 // and continue on as if nothing happened.
michael@0 180 NS_WARNING("Write failed (non-fatal)");
michael@0 181 // catch possible misuse of the input stream tee
michael@0 182 NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
michael@0 183 mSink = 0;
michael@0 184 break;
michael@0 185 }
michael@0 186 totalBytesWritten += bytesWritten;
michael@0 187 NS_ASSERTION(bytesWritten <= count, "wrote too much");
michael@0 188 count -= bytesWritten;
michael@0 189 }
michael@0 190 return NS_OK;
michael@0 191 }
michael@0 192 }
michael@0 193
michael@0 194 NS_METHOD
michael@0 195 nsInputStreamTee::WriteSegmentFun(nsIInputStream *in, void *closure, const char *fromSegment,
michael@0 196 uint32_t offset, uint32_t count, uint32_t *writeCount)
michael@0 197 {
michael@0 198 nsInputStreamTee *tee = reinterpret_cast<nsInputStreamTee *>(closure);
michael@0 199
michael@0 200 nsresult rv = tee->mWriter(in, tee->mClosure, fromSegment, offset, count, writeCount);
michael@0 201 if (NS_FAILED(rv) || (*writeCount == 0)) {
michael@0 202 NS_ASSERTION((NS_FAILED(rv) ? (*writeCount == 0) : true),
michael@0 203 "writer returned an error with non-zero writeCount");
michael@0 204 return rv;
michael@0 205 }
michael@0 206
michael@0 207 return tee->TeeSegment(fromSegment, *writeCount);
michael@0 208 }
michael@0 209
michael@0 210 NS_IMPL_ISUPPORTS(nsInputStreamTee,
michael@0 211 nsIInputStreamTee,
michael@0 212 nsIInputStream)
michael@0 213 NS_IMETHODIMP
michael@0 214 nsInputStreamTee::Close()
michael@0 215 {
michael@0 216 if (NS_WARN_IF(!mSource))
michael@0 217 return NS_ERROR_NOT_INITIALIZED;
michael@0 218 nsresult rv = mSource->Close();
michael@0 219 mSource = 0;
michael@0 220 mSink = 0;
michael@0 221 return rv;
michael@0 222 }
michael@0 223
michael@0 224 NS_IMETHODIMP
michael@0 225 nsInputStreamTee::Available(uint64_t *avail)
michael@0 226 {
michael@0 227 if (NS_WARN_IF(!mSource))
michael@0 228 return NS_ERROR_NOT_INITIALIZED;
michael@0 229 return mSource->Available(avail);
michael@0 230 }
michael@0 231
michael@0 232 NS_IMETHODIMP
michael@0 233 nsInputStreamTee::Read(char *buf, uint32_t count, uint32_t *bytesRead)
michael@0 234 {
michael@0 235 if (NS_WARN_IF(!mSource))
michael@0 236 return NS_ERROR_NOT_INITIALIZED;
michael@0 237
michael@0 238 nsresult rv = mSource->Read(buf, count, bytesRead);
michael@0 239 if (NS_FAILED(rv) || (*bytesRead == 0))
michael@0 240 return rv;
michael@0 241
michael@0 242 return TeeSegment(buf, *bytesRead);
michael@0 243 }
michael@0 244
michael@0 245 NS_IMETHODIMP
michael@0 246 nsInputStreamTee::ReadSegments(nsWriteSegmentFun writer,
michael@0 247 void *closure,
michael@0 248 uint32_t count,
michael@0 249 uint32_t *bytesRead)
michael@0 250 {
michael@0 251 if (NS_WARN_IF(!mSource))
michael@0 252 return NS_ERROR_NOT_INITIALIZED;
michael@0 253
michael@0 254 mWriter = writer;
michael@0 255 mClosure = closure;
michael@0 256
michael@0 257 return mSource->ReadSegments(WriteSegmentFun, this, count, bytesRead);
michael@0 258 }
michael@0 259
michael@0 260 NS_IMETHODIMP
michael@0 261 nsInputStreamTee::IsNonBlocking(bool *result)
michael@0 262 {
michael@0 263 if (NS_WARN_IF(!mSource))
michael@0 264 return NS_ERROR_NOT_INITIALIZED;
michael@0 265 return mSource->IsNonBlocking(result);
michael@0 266 }
michael@0 267
michael@0 268 NS_IMETHODIMP
michael@0 269 nsInputStreamTee::SetSource(nsIInputStream *source)
michael@0 270 {
michael@0 271 mSource = source;
michael@0 272 return NS_OK;
michael@0 273 }
michael@0 274
michael@0 275 NS_IMETHODIMP
michael@0 276 nsInputStreamTee::GetSource(nsIInputStream **source)
michael@0 277 {
michael@0 278 NS_IF_ADDREF(*source = mSource);
michael@0 279 return NS_OK;
michael@0 280 }
michael@0 281
michael@0 282 NS_IMETHODIMP
michael@0 283 nsInputStreamTee::SetSink(nsIOutputStream *sink)
michael@0 284 {
michael@0 285 #ifdef DEBUG
michael@0 286 if (sink) {
michael@0 287 bool nonBlocking;
michael@0 288 nsresult rv = sink->IsNonBlocking(&nonBlocking);
michael@0 289 if (NS_FAILED(rv) || nonBlocking)
michael@0 290 NS_ERROR("sink should be a blocking stream");
michael@0 291 }
michael@0 292 #endif
michael@0 293 mSink = sink;
michael@0 294 return NS_OK;
michael@0 295 }
michael@0 296
michael@0 297 NS_IMETHODIMP
michael@0 298 nsInputStreamTee::GetSink(nsIOutputStream **sink)
michael@0 299 {
michael@0 300 NS_IF_ADDREF(*sink = mSink);
michael@0 301 return NS_OK;
michael@0 302 }
michael@0 303
michael@0 304 NS_IMETHODIMP
michael@0 305 nsInputStreamTee::SetEventTarget(nsIEventTarget *anEventTarget)
michael@0 306 {
michael@0 307 mEventTarget = anEventTarget;
michael@0 308 if (mEventTarget) {
michael@0 309 // Only need synchronization if this is an async tee
michael@0 310 mLock = new Mutex("nsInputStreamTee.mLock");
michael@0 311 }
michael@0 312 return NS_OK;
michael@0 313 }
michael@0 314
michael@0 315 NS_IMETHODIMP
michael@0 316 nsInputStreamTee::GetEventTarget(nsIEventTarget **anEventTarget)
michael@0 317 {
michael@0 318 NS_IF_ADDREF(*anEventTarget = mEventTarget);
michael@0 319 return NS_OK;
michael@0 320 }
michael@0 321
michael@0 322
michael@0 323 nsresult
michael@0 324 NS_NewInputStreamTeeAsync(nsIInputStream **result,
michael@0 325 nsIInputStream *source,
michael@0 326 nsIOutputStream *sink,
michael@0 327 nsIEventTarget *anEventTarget)
michael@0 328 {
michael@0 329 nsresult rv;
michael@0 330
michael@0 331 nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
michael@0 332 if (!tee)
michael@0 333 return NS_ERROR_OUT_OF_MEMORY;
michael@0 334
michael@0 335 rv = tee->SetSource(source);
michael@0 336 if (NS_FAILED(rv)) return rv;
michael@0 337
michael@0 338 rv = tee->SetSink(sink);
michael@0 339 if (NS_FAILED(rv)) return rv;
michael@0 340
michael@0 341 rv = tee->SetEventTarget(anEventTarget);
michael@0 342 if (NS_FAILED(rv)) return rv;
michael@0 343
michael@0 344 NS_ADDREF(*result = tee);
michael@0 345 return rv;
michael@0 346 }
michael@0 347
michael@0 348 nsresult
michael@0 349 NS_NewInputStreamTee(nsIInputStream **result,
michael@0 350 nsIInputStream *source,
michael@0 351 nsIOutputStream *sink)
michael@0 352 {
michael@0 353 return NS_NewInputStreamTeeAsync(result, source, sink, nullptr);
michael@0 354 }
michael@0 355
michael@0 356 #undef LOG

mercurial