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