/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 | | |