1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is Mozilla.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Netscape Communications Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2002
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Darin Fisher <darin@netscape.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsStreamTransportService.h"
39 : #include "nsXPCOMCIDInternal.h"
40 : #include "nsNetSegmentUtils.h"
41 : #include "nsTransportUtils.h"
42 : #include "nsStreamUtils.h"
43 : #include "nsNetError.h"
44 : #include "nsNetCID.h"
45 :
46 : #include "nsIServiceManager.h"
47 : #include "nsIAsyncInputStream.h"
48 : #include "nsIAsyncOutputStream.h"
49 : #include "nsISeekableStream.h"
50 : #include "nsIPipe.h"
51 : #include "nsITransport.h"
52 : #include "nsIRunnable.h"
53 : #include "nsIObserverService.h"
54 : #include "mozilla/Services.h"
55 :
56 : //-----------------------------------------------------------------------------
57 : // nsInputStreamTransport
58 : //
59 : // Implements nsIInputStream as a wrapper around the real input stream. This
60 : // allows the transport to support seeking, range-limiting, progress reporting,
61 : // and close-when-done semantics while utilizing NS_AsyncCopy.
62 : //-----------------------------------------------------------------------------
63 :
64 : class nsInputStreamTransport : public nsITransport
65 : , public nsIInputStream
66 : {
67 : public:
68 : NS_DECL_ISUPPORTS
69 : NS_DECL_NSITRANSPORT
70 : NS_DECL_NSIINPUTSTREAM
71 :
72 281 : nsInputStreamTransport(nsIInputStream *source,
73 : PRUint64 offset,
74 : PRUint64 limit,
75 : bool closeWhenDone)
76 : : mSource(source)
77 : , mOffset(offset)
78 : , mLimit(limit)
79 : , mCloseWhenDone(closeWhenDone)
80 : , mFirstTime(true)
81 281 : , mInProgress(false)
82 : {
83 281 : }
84 :
85 562 : virtual ~nsInputStreamTransport()
86 281 : {
87 1124 : }
88 :
89 : private:
90 : nsCOMPtr<nsIAsyncInputStream> mPipeIn;
91 :
92 : // while the copy is active, these members may only be accessed from the
93 : // nsIInputStream implementation.
94 : nsCOMPtr<nsITransportEventSink> mEventSink;
95 : nsCOMPtr<nsIInputStream> mSource;
96 : PRUint64 mOffset;
97 : PRUint64 mLimit;
98 : bool mCloseWhenDone;
99 : bool mFirstTime;
100 :
101 : // this variable serves as a lock to prevent the state of the transport
102 : // from being modified once the copy is in progress.
103 : bool mInProgress;
104 : };
105 :
106 3220 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTransport,
107 : nsITransport,
108 : nsIInputStream)
109 :
110 : /** nsITransport **/
111 :
112 : NS_IMETHODIMP
113 281 : nsInputStreamTransport::OpenInputStream(PRUint32 flags,
114 : PRUint32 segsize,
115 : PRUint32 segcount,
116 : nsIInputStream **result)
117 : {
118 281 : NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
119 :
120 : nsresult rv;
121 : nsCOMPtr<nsIEventTarget> target =
122 562 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
123 281 : if (NS_FAILED(rv)) return rv;
124 :
125 : // XXX if the caller requests an unbuffered stream, then perhaps
126 : // we'd want to simply return mSource; however, then we would
127 : // not be reading mSource on a background thread. is this ok?
128 :
129 281 : bool nonblocking = !(flags & OPEN_BLOCKING);
130 :
131 281 : net_ResolveSegmentParams(segsize, segcount);
132 :
133 562 : nsCOMPtr<nsIAsyncOutputStream> pipeOut;
134 281 : rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
135 281 : getter_AddRefs(pipeOut),
136 : nonblocking, true,
137 281 : segsize, segcount);
138 281 : if (NS_FAILED(rv)) return rv;
139 :
140 281 : mInProgress = true;
141 :
142 : // startup async copy process...
143 : rv = NS_AsyncCopy(this, pipeOut, target,
144 281 : NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
145 281 : if (NS_SUCCEEDED(rv))
146 281 : NS_ADDREF(*result = mPipeIn);
147 :
148 281 : return rv;
149 : }
150 :
151 : NS_IMETHODIMP
152 0 : nsInputStreamTransport::OpenOutputStream(PRUint32 flags,
153 : PRUint32 segsize,
154 : PRUint32 segcount,
155 : nsIOutputStream **result)
156 : {
157 : // this transport only supports reading!
158 0 : NS_NOTREACHED("nsInputStreamTransport::OpenOutputStream");
159 0 : return NS_ERROR_UNEXPECTED;
160 : }
161 :
162 : NS_IMETHODIMP
163 17 : nsInputStreamTransport::Close(nsresult reason)
164 : {
165 17 : if (NS_SUCCEEDED(reason))
166 0 : reason = NS_BASE_STREAM_CLOSED;
167 :
168 17 : return mPipeIn->CloseWithStatus(reason);
169 : }
170 :
171 : NS_IMETHODIMP
172 17 : nsInputStreamTransport::SetEventSink(nsITransportEventSink *sink,
173 : nsIEventTarget *target)
174 : {
175 17 : NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
176 :
177 17 : if (target)
178 17 : return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
179 17 : sink, target);
180 :
181 0 : mEventSink = sink;
182 0 : return NS_OK;
183 : }
184 :
185 : /** nsIInputStream **/
186 :
187 : NS_IMETHODIMP
188 281 : nsInputStreamTransport::Close()
189 : {
190 281 : if (mCloseWhenDone)
191 281 : mSource->Close();
192 :
193 : // make additional reads return early...
194 281 : mOffset = mLimit = 0;
195 281 : return NS_OK;
196 : }
197 :
198 : NS_IMETHODIMP
199 0 : nsInputStreamTransport::Available(PRUint32 *result)
200 : {
201 0 : return NS_ERROR_NOT_IMPLEMENTED;
202 : }
203 :
204 : NS_IMETHODIMP
205 563 : nsInputStreamTransport::Read(char *buf, PRUint32 count, PRUint32 *result)
206 : {
207 563 : if (mFirstTime) {
208 281 : mFirstTime = false;
209 281 : if (mOffset != 0) {
210 : // read from current position if offset equal to max
211 281 : if (mOffset != LL_MAXUINT) {
212 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
213 0 : if (seekable)
214 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
215 : }
216 : // reset offset to zero so we can use it to enforce limit
217 281 : mOffset = 0;
218 : }
219 : }
220 :
221 : // limit amount read
222 563 : PRUint32 max = mLimit - mOffset;
223 563 : if (max == 0) {
224 0 : *result = 0;
225 0 : return NS_OK;
226 : }
227 :
228 563 : if (count > max)
229 0 : count = max;
230 :
231 563 : nsresult rv = mSource->Read(buf, count, result);
232 :
233 563 : if (NS_SUCCEEDED(rv)) {
234 563 : mOffset += *result;
235 563 : if (mEventSink)
236 34 : mEventSink->OnTransportStatus(this, STATUS_READING, mOffset, mLimit);
237 : }
238 563 : return rv;
239 : }
240 :
241 : NS_IMETHODIMP
242 0 : nsInputStreamTransport::ReadSegments(nsWriteSegmentFun writer, void *closure,
243 : PRUint32 count, PRUint32 *result)
244 : {
245 0 : return NS_ERROR_NOT_IMPLEMENTED;
246 : }
247 :
248 : NS_IMETHODIMP
249 0 : nsInputStreamTransport::IsNonBlocking(bool *result)
250 : {
251 0 : *result = false;
252 0 : return NS_OK;
253 : }
254 :
255 : //-----------------------------------------------------------------------------
256 : // nsOutputStreamTransport
257 : //
258 : // Implements nsIOutputStream as a wrapper around the real input stream. This
259 : // allows the transport to support seeking, range-limiting, progress reporting,
260 : // and close-when-done semantics while utilizing NS_AsyncCopy.
261 : //-----------------------------------------------------------------------------
262 :
263 : class nsOutputStreamTransport : public nsITransport
264 : , public nsIOutputStream
265 : {
266 : public:
267 : NS_DECL_ISUPPORTS
268 : NS_DECL_NSITRANSPORT
269 : NS_DECL_NSIOUTPUTSTREAM
270 :
271 0 : nsOutputStreamTransport(nsIOutputStream *sink,
272 : PRUint64 offset,
273 : PRUint64 limit,
274 : bool closeWhenDone)
275 : : mSink(sink)
276 : , mOffset(offset)
277 : , mLimit(limit)
278 : , mCloseWhenDone(closeWhenDone)
279 : , mFirstTime(true)
280 0 : , mInProgress(false)
281 : {
282 0 : }
283 :
284 0 : virtual ~nsOutputStreamTransport()
285 0 : {
286 0 : }
287 :
288 : private:
289 : nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
290 :
291 : // while the copy is active, these members may only be accessed from the
292 : // nsIOutputStream implementation.
293 : nsCOMPtr<nsITransportEventSink> mEventSink;
294 : nsCOMPtr<nsIOutputStream> mSink;
295 : PRUint64 mOffset;
296 : PRUint64 mLimit;
297 : bool mCloseWhenDone;
298 : bool mFirstTime;
299 :
300 : // this variable serves as a lock to prevent the state of the transport
301 : // from being modified once the copy is in progress.
302 : bool mInProgress;
303 : };
304 :
305 0 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamTransport,
306 : nsITransport,
307 : nsIOutputStream)
308 :
309 : /** nsITransport **/
310 :
311 : NS_IMETHODIMP
312 0 : nsOutputStreamTransport::OpenInputStream(PRUint32 flags,
313 : PRUint32 segsize,
314 : PRUint32 segcount,
315 : nsIInputStream **result)
316 : {
317 : // this transport only supports writing!
318 0 : NS_NOTREACHED("nsOutputStreamTransport::OpenInputStream");
319 0 : return NS_ERROR_UNEXPECTED;
320 : }
321 :
322 : NS_IMETHODIMP
323 0 : nsOutputStreamTransport::OpenOutputStream(PRUint32 flags,
324 : PRUint32 segsize,
325 : PRUint32 segcount,
326 : nsIOutputStream **result)
327 : {
328 0 : NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
329 :
330 : nsresult rv;
331 : nsCOMPtr<nsIEventTarget> target =
332 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
333 0 : if (NS_FAILED(rv)) return rv;
334 :
335 : // XXX if the caller requests an unbuffered stream, then perhaps
336 : // we'd want to simply return mSink; however, then we would
337 : // not be writing to mSink on a background thread. is this ok?
338 :
339 0 : bool nonblocking = !(flags & OPEN_BLOCKING);
340 :
341 0 : net_ResolveSegmentParams(segsize, segcount);
342 :
343 0 : nsCOMPtr<nsIAsyncInputStream> pipeIn;
344 0 : rv = NS_NewPipe2(getter_AddRefs(pipeIn),
345 0 : getter_AddRefs(mPipeOut),
346 : true, nonblocking,
347 0 : segsize, segcount);
348 0 : if (NS_FAILED(rv)) return rv;
349 :
350 0 : mInProgress = true;
351 :
352 : // startup async copy process...
353 : rv = NS_AsyncCopy(pipeIn, this, target,
354 0 : NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
355 0 : if (NS_SUCCEEDED(rv))
356 0 : NS_ADDREF(*result = mPipeOut);
357 :
358 0 : return rv;
359 : }
360 :
361 : NS_IMETHODIMP
362 0 : nsOutputStreamTransport::Close(nsresult reason)
363 : {
364 0 : if (NS_SUCCEEDED(reason))
365 0 : reason = NS_BASE_STREAM_CLOSED;
366 :
367 0 : return mPipeOut->CloseWithStatus(reason);
368 : }
369 :
370 : NS_IMETHODIMP
371 0 : nsOutputStreamTransport::SetEventSink(nsITransportEventSink *sink,
372 : nsIEventTarget *target)
373 : {
374 0 : NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
375 :
376 0 : if (target)
377 0 : return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
378 0 : sink, target);
379 :
380 0 : mEventSink = sink;
381 0 : return NS_OK;
382 : }
383 :
384 : /** nsIOutputStream **/
385 :
386 : NS_IMETHODIMP
387 0 : nsOutputStreamTransport::Close()
388 : {
389 0 : if (mCloseWhenDone)
390 0 : mSink->Close();
391 :
392 : // make additional writes return early...
393 0 : mOffset = mLimit = 0;
394 0 : return NS_OK;
395 : }
396 :
397 : NS_IMETHODIMP
398 0 : nsOutputStreamTransport::Flush()
399 : {
400 0 : return NS_OK;
401 : }
402 :
403 : NS_IMETHODIMP
404 0 : nsOutputStreamTransport::Write(const char *buf, PRUint32 count, PRUint32 *result)
405 : {
406 0 : if (mFirstTime) {
407 0 : mFirstTime = false;
408 0 : if (mOffset != 0) {
409 : // write to current position if offset equal to max
410 0 : if (mOffset != LL_MAXUINT) {
411 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
412 0 : if (seekable)
413 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
414 : }
415 : // reset offset to zero so we can use it to enforce limit
416 0 : mOffset = 0;
417 : }
418 : }
419 :
420 : // limit amount written
421 0 : PRUint32 max = mLimit - mOffset;
422 0 : if (max == 0) {
423 0 : *result = 0;
424 0 : return NS_OK;
425 : }
426 :
427 0 : if (count > max)
428 0 : count = max;
429 :
430 0 : nsresult rv = mSink->Write(buf, count, result);
431 :
432 0 : if (NS_SUCCEEDED(rv)) {
433 0 : mOffset += *result;
434 0 : if (mEventSink)
435 0 : mEventSink->OnTransportStatus(this, STATUS_WRITING, mOffset, mLimit);
436 : }
437 0 : return rv;
438 : }
439 :
440 : NS_IMETHODIMP
441 0 : nsOutputStreamTransport::WriteSegments(nsReadSegmentFun reader, void *closure,
442 : PRUint32 count, PRUint32 *result)
443 : {
444 0 : return NS_ERROR_NOT_IMPLEMENTED;
445 : }
446 :
447 : NS_IMETHODIMP
448 0 : nsOutputStreamTransport::WriteFrom(nsIInputStream *in, PRUint32 count, PRUint32 *result)
449 : {
450 0 : return NS_ERROR_NOT_IMPLEMENTED;
451 : }
452 :
453 : NS_IMETHODIMP
454 0 : nsOutputStreamTransport::IsNonBlocking(bool *result)
455 : {
456 0 : *result = false;
457 0 : return NS_OK;
458 : }
459 :
460 : //-----------------------------------------------------------------------------
461 : // nsStreamTransportService
462 : //-----------------------------------------------------------------------------
463 :
464 168 : nsStreamTransportService::~nsStreamTransportService()
465 : {
466 84 : NS_ASSERTION(!mPool, "thread pool wasn't shutdown");
467 84 : }
468 :
469 : nsresult
470 84 : nsStreamTransportService::Init()
471 : {
472 84 : mPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
473 84 : NS_ENSURE_STATE(mPool);
474 :
475 : // Configure the pool
476 84 : mPool->SetThreadLimit(4);
477 84 : mPool->SetIdleThreadLimit(1);
478 84 : mPool->SetIdleThreadTimeout(PR_SecondsToInterval(60));
479 :
480 : nsCOMPtr<nsIObserverService> obsSvc =
481 168 : mozilla::services::GetObserverService();
482 84 : if (obsSvc)
483 84 : obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
484 84 : return NS_OK;
485 : }
486 :
487 9134 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsStreamTransportService,
488 : nsIStreamTransportService,
489 : nsIEventTarget,
490 : nsIObserver)
491 :
492 : NS_IMETHODIMP
493 722 : nsStreamTransportService::Dispatch(nsIRunnable *task, PRUint32 flags)
494 : {
495 722 : NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED);
496 722 : return mPool->Dispatch(task, flags);
497 : }
498 :
499 : NS_IMETHODIMP
500 0 : nsStreamTransportService::IsOnCurrentThread(bool *result)
501 : {
502 0 : NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED);
503 0 : return mPool->IsOnCurrentThread(result);
504 : }
505 :
506 : NS_IMETHODIMP
507 281 : nsStreamTransportService::CreateInputTransport(nsIInputStream *stream,
508 : PRInt64 offset,
509 : PRInt64 limit,
510 : bool closeWhenDone,
511 : nsITransport **result)
512 : {
513 : nsInputStreamTransport *trans =
514 281 : new nsInputStreamTransport(stream, offset, limit, closeWhenDone);
515 281 : if (!trans)
516 0 : return NS_ERROR_OUT_OF_MEMORY;
517 281 : NS_ADDREF(*result = trans);
518 281 : return NS_OK;
519 : }
520 :
521 : NS_IMETHODIMP
522 0 : nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream,
523 : PRInt64 offset,
524 : PRInt64 limit,
525 : bool closeWhenDone,
526 : nsITransport **result)
527 : {
528 : nsOutputStreamTransport *trans =
529 0 : new nsOutputStreamTransport(stream, offset, limit, closeWhenDone);
530 0 : if (!trans)
531 0 : return NS_ERROR_OUT_OF_MEMORY;
532 0 : NS_ADDREF(*result = trans);
533 0 : return NS_OK;
534 : }
535 :
536 : NS_IMETHODIMP
537 84 : nsStreamTransportService::Observe(nsISupports *subject, const char *topic,
538 : const PRUnichar *data)
539 : {
540 84 : NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
541 :
542 84 : if (mPool) {
543 84 : mPool->Shutdown();
544 84 : mPool = nsnull;
545 : }
546 84 : return NS_OK;
547 : }
|