Coverage Report

Created: 2018-09-25 14:53

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