netwerk/base/src/nsAsyncStreamCopier.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "nsAsyncStreamCopier.h"
michael@0 6 #include "nsIOService.h"
michael@0 7 #include "nsIEventTarget.h"
michael@0 8 #include "nsStreamUtils.h"
michael@0 9 #include "nsThreadUtils.h"
michael@0 10 #include "nsNetUtil.h"
michael@0 11 #include "prlog.h"
michael@0 12
michael@0 13 using namespace mozilla;
michael@0 14
michael@0 15 #undef LOG
michael@0 16 #if defined(PR_LOGGING)
michael@0 17 //
michael@0 18 // NSPR_LOG_MODULES=nsStreamCopier:5
michael@0 19 //
michael@0 20 static PRLogModuleInfo *gStreamCopierLog = nullptr;
michael@0 21 #endif
michael@0 22 #define LOG(args) PR_LOG(gStreamCopierLog, PR_LOG_DEBUG, args)
michael@0 23
michael@0 24 /**
michael@0 25 * An event used to perform initialization off the main thread.
michael@0 26 */
michael@0 27 class AsyncApplyBufferingPolicyEvent MOZ_FINAL: public nsRunnable
michael@0 28 {
michael@0 29 public:
michael@0 30 /**
michael@0 31 * @param aCopier
michael@0 32 * The nsAsyncStreamCopier requesting the information.
michael@0 33 */
michael@0 34 AsyncApplyBufferingPolicyEvent(nsAsyncStreamCopier* aCopier)
michael@0 35 : mCopier(aCopier)
michael@0 36 , mTarget(NS_GetCurrentThread())
michael@0 37 { }
michael@0 38 NS_METHOD Run()
michael@0 39 {
michael@0 40 nsresult rv = mCopier->ApplyBufferingPolicy();
michael@0 41 if (NS_FAILED(rv)) {
michael@0 42 mCopier->Cancel(rv);
michael@0 43 return NS_OK;
michael@0 44 }
michael@0 45
michael@0 46 nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mCopier, &nsAsyncStreamCopier::AsyncCopyInternal);
michael@0 47 rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 48 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 49
michael@0 50 if (NS_FAILED(rv)) {
michael@0 51 mCopier->Cancel(rv);
michael@0 52 }
michael@0 53 return NS_OK;
michael@0 54 }
michael@0 55 private:
michael@0 56 nsRefPtr<nsAsyncStreamCopier> mCopier;
michael@0 57 nsCOMPtr<nsIEventTarget> mTarget;
michael@0 58 };
michael@0 59
michael@0 60
michael@0 61
michael@0 62 //-----------------------------------------------------------------------------
michael@0 63
michael@0 64 nsAsyncStreamCopier::nsAsyncStreamCopier()
michael@0 65 : mLock("nsAsyncStreamCopier.mLock")
michael@0 66 , mMode(NS_ASYNCCOPY_VIA_READSEGMENTS)
michael@0 67 , mChunkSize(nsIOService::gDefaultSegmentSize)
michael@0 68 , mStatus(NS_OK)
michael@0 69 , mIsPending(false)
michael@0 70 , mShouldSniffBuffering(false)
michael@0 71 {
michael@0 72 #if defined(PR_LOGGING)
michael@0 73 if (!gStreamCopierLog)
michael@0 74 gStreamCopierLog = PR_NewLogModule("nsStreamCopier");
michael@0 75 #endif
michael@0 76 LOG(("Creating nsAsyncStreamCopier @%x\n", this));
michael@0 77 }
michael@0 78
michael@0 79 nsAsyncStreamCopier::~nsAsyncStreamCopier()
michael@0 80 {
michael@0 81 LOG(("Destroying nsAsyncStreamCopier @%x\n", this));
michael@0 82 }
michael@0 83
michael@0 84 bool
michael@0 85 nsAsyncStreamCopier::IsComplete(nsresult *status)
michael@0 86 {
michael@0 87 MutexAutoLock lock(mLock);
michael@0 88 if (status)
michael@0 89 *status = mStatus;
michael@0 90 return !mIsPending;
michael@0 91 }
michael@0 92
michael@0 93 nsIRequest*
michael@0 94 nsAsyncStreamCopier::AsRequest()
michael@0 95 {
michael@0 96 return static_cast<nsIRequest*>(static_cast<nsIAsyncStreamCopier*>(this));
michael@0 97 }
michael@0 98
michael@0 99 void
michael@0 100 nsAsyncStreamCopier::Complete(nsresult status)
michael@0 101 {
michael@0 102 LOG(("nsAsyncStreamCopier::Complete [this=%p status=%x]\n", this, status));
michael@0 103
michael@0 104 nsCOMPtr<nsIRequestObserver> observer;
michael@0 105 nsCOMPtr<nsISupports> ctx;
michael@0 106 {
michael@0 107 MutexAutoLock lock(mLock);
michael@0 108 mCopierCtx = nullptr;
michael@0 109
michael@0 110 if (mIsPending) {
michael@0 111 mIsPending = false;
michael@0 112 mStatus = status;
michael@0 113
michael@0 114 // setup OnStopRequest callback and release references...
michael@0 115 observer = mObserver;
michael@0 116 mObserver = nullptr;
michael@0 117 }
michael@0 118 }
michael@0 119
michael@0 120 if (observer) {
michael@0 121 LOG((" calling OnStopRequest [status=%x]\n", status));
michael@0 122 observer->OnStopRequest(AsRequest(), ctx, status);
michael@0 123 }
michael@0 124 }
michael@0 125
michael@0 126 void
michael@0 127 nsAsyncStreamCopier::OnAsyncCopyComplete(void *closure, nsresult status)
michael@0 128 {
michael@0 129 nsAsyncStreamCopier *self = (nsAsyncStreamCopier *) closure;
michael@0 130 self->Complete(status);
michael@0 131 NS_RELEASE(self); // addref'd in AsyncCopy
michael@0 132 }
michael@0 133
michael@0 134 //-----------------------------------------------------------------------------
michael@0 135 // nsISupports
michael@0 136
michael@0 137 // We cannot use simply NS_IMPL_ISUPPORTSx as both
michael@0 138 // nsIAsyncStreamCopier and nsIAsyncStreamCopier2 implement nsIRequest
michael@0 139
michael@0 140 NS_IMPL_ADDREF(nsAsyncStreamCopier)
michael@0 141 NS_IMPL_RELEASE(nsAsyncStreamCopier)
michael@0 142 NS_INTERFACE_TABLE_HEAD(nsAsyncStreamCopier)
michael@0 143 NS_INTERFACE_TABLE_BEGIN
michael@0 144 NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier)
michael@0 145 NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier2)
michael@0 146 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsIRequest, nsIAsyncStreamCopier)
michael@0 147 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsISupports, nsIAsyncStreamCopier)
michael@0 148 NS_INTERFACE_TABLE_END
michael@0 149 NS_INTERFACE_TABLE_TAIL
michael@0 150
michael@0 151 //-----------------------------------------------------------------------------
michael@0 152 // nsIRequest
michael@0 153
michael@0 154 NS_IMETHODIMP
michael@0 155 nsAsyncStreamCopier::GetName(nsACString &name)
michael@0 156 {
michael@0 157 name.Truncate();
michael@0 158 return NS_OK;
michael@0 159 }
michael@0 160
michael@0 161 NS_IMETHODIMP
michael@0 162 nsAsyncStreamCopier::IsPending(bool *result)
michael@0 163 {
michael@0 164 *result = !IsComplete();
michael@0 165 return NS_OK;
michael@0 166 }
michael@0 167
michael@0 168 NS_IMETHODIMP
michael@0 169 nsAsyncStreamCopier::GetStatus(nsresult *status)
michael@0 170 {
michael@0 171 IsComplete(status);
michael@0 172 return NS_OK;
michael@0 173 }
michael@0 174
michael@0 175 NS_IMETHODIMP
michael@0 176 nsAsyncStreamCopier::Cancel(nsresult status)
michael@0 177 {
michael@0 178 nsCOMPtr<nsISupports> copierCtx;
michael@0 179 {
michael@0 180 MutexAutoLock lock(mLock);
michael@0 181 if (!mIsPending)
michael@0 182 return NS_OK;
michael@0 183 copierCtx.swap(mCopierCtx);
michael@0 184 }
michael@0 185
michael@0 186 if (NS_SUCCEEDED(status)) {
michael@0 187 NS_WARNING("cancel with non-failure status code");
michael@0 188 status = NS_BASE_STREAM_CLOSED;
michael@0 189 }
michael@0 190
michael@0 191 if (copierCtx)
michael@0 192 NS_CancelAsyncCopy(copierCtx, status);
michael@0 193
michael@0 194 return NS_OK;
michael@0 195 }
michael@0 196
michael@0 197 NS_IMETHODIMP
michael@0 198 nsAsyncStreamCopier::Suspend()
michael@0 199 {
michael@0 200 NS_NOTREACHED("nsAsyncStreamCopier::Suspend");
michael@0 201 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 202 }
michael@0 203
michael@0 204 NS_IMETHODIMP
michael@0 205 nsAsyncStreamCopier::Resume()
michael@0 206 {
michael@0 207 NS_NOTREACHED("nsAsyncStreamCopier::Resume");
michael@0 208 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 209 }
michael@0 210
michael@0 211 NS_IMETHODIMP
michael@0 212 nsAsyncStreamCopier::GetLoadFlags(nsLoadFlags *aLoadFlags)
michael@0 213 {
michael@0 214 *aLoadFlags = LOAD_NORMAL;
michael@0 215 return NS_OK;
michael@0 216 }
michael@0 217
michael@0 218 NS_IMETHODIMP
michael@0 219 nsAsyncStreamCopier::SetLoadFlags(nsLoadFlags aLoadFlags)
michael@0 220 {
michael@0 221 return NS_OK;
michael@0 222 }
michael@0 223
michael@0 224 NS_IMETHODIMP
michael@0 225 nsAsyncStreamCopier::GetLoadGroup(nsILoadGroup **aLoadGroup)
michael@0 226 {
michael@0 227 *aLoadGroup = nullptr;
michael@0 228 return NS_OK;
michael@0 229 }
michael@0 230
michael@0 231 NS_IMETHODIMP
michael@0 232 nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup *aLoadGroup)
michael@0 233 {
michael@0 234 return NS_OK;
michael@0 235 }
michael@0 236
michael@0 237 nsresult
michael@0 238 nsAsyncStreamCopier::InitInternal(nsIInputStream *source,
michael@0 239 nsIOutputStream *sink,
michael@0 240 nsIEventTarget *target,
michael@0 241 uint32_t chunkSize,
michael@0 242 bool closeSource,
michael@0 243 bool closeSink)
michael@0 244 {
michael@0 245 NS_ASSERTION(!mSource && !mSink, "Init() called more than once");
michael@0 246 if (chunkSize == 0) {
michael@0 247 chunkSize = nsIOService::gDefaultSegmentSize;
michael@0 248 }
michael@0 249 mChunkSize = chunkSize;
michael@0 250
michael@0 251 mSource = source;
michael@0 252 mSink = sink;
michael@0 253 mCloseSource = closeSource;
michael@0 254 mCloseSink = closeSink;
michael@0 255
michael@0 256 if (target) {
michael@0 257 mTarget = target;
michael@0 258 } else {
michael@0 259 nsresult rv;
michael@0 260 mTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
michael@0 261 if (NS_FAILED(rv)) {
michael@0 262 return rv;
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 return NS_OK;
michael@0 267 }
michael@0 268
michael@0 269 //-----------------------------------------------------------------------------
michael@0 270 // nsIAsyncStreamCopier
michael@0 271
michael@0 272 NS_IMETHODIMP
michael@0 273 nsAsyncStreamCopier::Init(nsIInputStream *source,
michael@0 274 nsIOutputStream *sink,
michael@0 275 nsIEventTarget *target,
michael@0 276 bool sourceBuffered,
michael@0 277 bool sinkBuffered,
michael@0 278 uint32_t chunkSize,
michael@0 279 bool closeSource,
michael@0 280 bool closeSink)
michael@0 281 {
michael@0 282 NS_ASSERTION(sourceBuffered || sinkBuffered, "at least one stream must be buffered");
michael@0 283 mMode = sourceBuffered ? NS_ASYNCCOPY_VIA_READSEGMENTS
michael@0 284 : NS_ASYNCCOPY_VIA_WRITESEGMENTS;
michael@0 285
michael@0 286 return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
michael@0 287 }
michael@0 288
michael@0 289 //-----------------------------------------------------------------------------
michael@0 290 // nsIAsyncStreamCopier2
michael@0 291
michael@0 292 NS_IMETHODIMP
michael@0 293 nsAsyncStreamCopier::Init(nsIInputStream *source,
michael@0 294 nsIOutputStream *sink,
michael@0 295 nsIEventTarget *target,
michael@0 296 uint32_t chunkSize,
michael@0 297 bool closeSource,
michael@0 298 bool closeSink)
michael@0 299 {
michael@0 300 mShouldSniffBuffering = true;
michael@0 301
michael@0 302 return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
michael@0 303 }
michael@0 304
michael@0 305 /**
michael@0 306 * Detect whether the input or the output stream is buffered,
michael@0 307 * bufferize one of them if neither is buffered.
michael@0 308 */
michael@0 309 nsresult
michael@0 310 nsAsyncStreamCopier::ApplyBufferingPolicy()
michael@0 311 {
michael@0 312 // This function causes I/O, it must not be executed on the main
michael@0 313 // thread.
michael@0 314 MOZ_ASSERT(!NS_IsMainThread());
michael@0 315
michael@0 316 if (NS_OutputStreamIsBuffered(mSink)) {
michael@0 317 // Sink is buffered, no need to perform additional buffering
michael@0 318 mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
michael@0 319 return NS_OK;
michael@0 320 }
michael@0 321 if (NS_InputStreamIsBuffered(mSource)) {
michael@0 322 // Source is buffered, no need to perform additional buffering
michael@0 323 mMode = NS_ASYNCCOPY_VIA_READSEGMENTS;
michael@0 324 return NS_OK;
michael@0 325 }
michael@0 326
michael@0 327 // No buffering, let's buffer the sink
michael@0 328 nsresult rv;
michael@0 329 nsCOMPtr<nsIBufferedOutputStream> sink =
michael@0 330 do_CreateInstance(NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &rv);
michael@0 331 if (NS_FAILED(rv)) {
michael@0 332 return rv;
michael@0 333 }
michael@0 334
michael@0 335 rv = sink->Init(mSink, mChunkSize);
michael@0 336 if (NS_FAILED(rv)) {
michael@0 337 return rv;
michael@0 338 }
michael@0 339
michael@0 340 mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
michael@0 341 mSink = sink;
michael@0 342 return NS_OK;
michael@0 343 }
michael@0 344
michael@0 345 //-----------------------------------------------------------------------------
michael@0 346 // Both nsIAsyncStreamCopier and nsIAsyncStreamCopier2
michael@0 347
michael@0 348 NS_IMETHODIMP
michael@0 349 nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx)
michael@0 350 {
michael@0 351 LOG(("nsAsyncStreamCopier::AsyncCopy [this=%p observer=%x]\n", this, observer));
michael@0 352
michael@0 353 NS_ASSERTION(mSource && mSink, "not initialized");
michael@0 354 nsresult rv;
michael@0 355
michael@0 356 if (observer) {
michael@0 357 // build proxy for observer events
michael@0 358 rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), observer, ctx);
michael@0 359 if (NS_FAILED(rv)) return rv;
michael@0 360 }
michael@0 361
michael@0 362 // from this point forward, AsyncCopy is going to return NS_OK. any errors
michael@0 363 // will be reported via OnStopRequest.
michael@0 364 mIsPending = true;
michael@0 365
michael@0 366 if (mObserver) {
michael@0 367 rv = mObserver->OnStartRequest(AsRequest(), nullptr);
michael@0 368 if (NS_FAILED(rv))
michael@0 369 Cancel(rv);
michael@0 370 }
michael@0 371
michael@0 372 if (!mShouldSniffBuffering) {
michael@0 373 // No buffer sniffing required, let's proceed
michael@0 374 AsyncCopyInternal();
michael@0 375 return NS_OK;
michael@0 376 }
michael@0 377
michael@0 378 if (NS_IsMainThread()) {
michael@0 379 // Don't perform buffer sniffing on the main thread
michael@0 380 nsCOMPtr<AsyncApplyBufferingPolicyEvent> event
michael@0 381 = new AsyncApplyBufferingPolicyEvent(this);
michael@0 382 rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 383 if (NS_FAILED(rv)) {
michael@0 384 Cancel(rv);
michael@0 385 }
michael@0 386 return NS_OK;
michael@0 387 }
michael@0 388
michael@0 389 // We're not going to block the main thread, so let's sniff here
michael@0 390 rv = ApplyBufferingPolicy();
michael@0 391 if (NS_FAILED(rv)) {
michael@0 392 Cancel(rv);
michael@0 393 }
michael@0 394 AsyncCopyInternal();
michael@0 395 return NS_OK;
michael@0 396 }
michael@0 397
michael@0 398 // Launch async copy.
michael@0 399 // All errors are reported through the observer.
michael@0 400 void
michael@0 401 nsAsyncStreamCopier::AsyncCopyInternal()
michael@0 402 {
michael@0 403 MOZ_ASSERT(mMode == NS_ASYNCCOPY_VIA_READSEGMENTS
michael@0 404 || mMode == NS_ASYNCCOPY_VIA_WRITESEGMENTS);
michael@0 405
michael@0 406 nsresult rv;
michael@0 407 // we want to receive progress notifications; release happens in
michael@0 408 // OnAsyncCopyComplete.
michael@0 409 NS_ADDREF_THIS();
michael@0 410 {
michael@0 411 MutexAutoLock lock(mLock);
michael@0 412 rv = NS_AsyncCopy(mSource, mSink, mTarget, mMode, mChunkSize,
michael@0 413 OnAsyncCopyComplete, this, mCloseSource, mCloseSink,
michael@0 414 getter_AddRefs(mCopierCtx));
michael@0 415 }
michael@0 416 if (NS_FAILED(rv)) {
michael@0 417 NS_RELEASE_THIS();
michael@0 418 Cancel(rv);
michael@0 419 }
michael@0 420 }
michael@0 421
michael@0 422

mercurial