Back to index

lightning-sunbird  0.9+nobinonly
nsStreamTransportService.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsStreamTransportService.h"
00039 #include "nsNetSegmentUtils.h"
00040 #include "nsAutoLock.h"
00041 #include "nsInt64.h"
00042 #include "nsTransportUtils.h"
00043 #include "nsStreamUtils.h"
00044 #include "nsNetError.h"
00045 #include "nsNetCID.h"
00046 
00047 #include "nsIServiceManager.h"
00048 #include "nsIAsyncInputStream.h"
00049 #include "nsIAsyncOutputStream.h"
00050 #include "nsISeekableStream.h"
00051 #include "nsIPipe.h"
00052 #include "nsITransport.h"
00053 #include "nsIRunnable.h"
00054 #include "nsIProxyObjectManager.h"
00055 #include "nsIEventTarget.h"
00056 
00057 //-----------------------------------------------------------------------------
00058 // nsInputStreamTransport
00059 //
00060 // Implements nsIInputStream as a wrapper around the real input stream.  This
00061 // allows the transport to support seeking, range-limiting, progress reporting,
00062 // and close-when-done semantics while utilizing NS_AsyncCopy.
00063 //-----------------------------------------------------------------------------
00064 
00065 class nsInputStreamTransport : public nsITransport
00066                              , public nsIInputStream
00067 {
00068 public:
00069     NS_DECL_ISUPPORTS
00070     NS_DECL_NSITRANSPORT
00071     NS_DECL_NSIINPUTSTREAM
00072 
00073     nsInputStreamTransport(nsIInputStream *source,
00074                            PRUint64 offset,
00075                            PRUint64 limit,
00076                            PRBool closeWhenDone)
00077         : mSource(source)
00078         , mOffset(offset)
00079         , mLimit(limit)
00080         , mCloseWhenDone(closeWhenDone)
00081         , mFirstTime(PR_TRUE)
00082         , mInProgress(PR_FALSE)
00083     {
00084     }
00085 
00086     virtual ~nsInputStreamTransport()
00087     {
00088     }
00089 
00090 private:
00091     nsCOMPtr<nsIAsyncInputStream>   mPipeIn;
00092 
00093     // while the copy is active, these members may only be accessed from the
00094     // nsIInputStream implementation.
00095     nsCOMPtr<nsITransportEventSink> mEventSink;
00096     nsCOMPtr<nsIInputStream>        mSource;
00097     nsUint64                        mOffset;
00098     nsUint64                        mLimit;
00099     PRPackedBool                    mCloseWhenDone;
00100     PRPackedBool                    mFirstTime;
00101 
00102     // this variable serves as a lock to prevent the state of the transport
00103     // from being modified once the copy is in progress.
00104     PRPackedBool                    mInProgress;
00105 };
00106 
00107 NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTransport,
00108                               nsITransport,
00109                               nsIInputStream)
00110 
00111 
00113 NS_IMETHODIMP
00114 nsInputStreamTransport::OpenInputStream(PRUint32 flags,
00115                                         PRUint32 segsize,
00116                                         PRUint32 segcount,
00117                                         nsIInputStream **result)
00118 {
00119     NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
00120 
00121     nsresult rv;
00122     nsCOMPtr<nsIEventTarget> target =
00123             do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv);
00124     if (NS_FAILED(rv)) return rv;
00125 
00126     // XXX if the caller requests an unbuffered stream, then perhaps
00127     //     we'd want to simply return mSource; however, then we would
00128     //     not be reading mSource on a background thread.  is this ok?
00129  
00130     PRBool nonblocking = !(flags & OPEN_BLOCKING);
00131 
00132     net_ResolveSegmentParams(segsize, segcount);
00133     nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
00134 
00135     nsCOMPtr<nsIAsyncOutputStream> pipeOut;
00136     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
00137                      getter_AddRefs(pipeOut),
00138                      nonblocking, PR_TRUE,
00139                      segsize, segcount, segalloc);
00140     if (NS_FAILED(rv)) return rv;
00141 
00142     mInProgress = PR_TRUE;
00143 
00144     // startup async copy process...
00145     rv = NS_AsyncCopy(this, pipeOut, target,
00146                       NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
00147     if (NS_SUCCEEDED(rv))
00148         NS_ADDREF(*result = mPipeIn);
00149 
00150     return rv;
00151 }
00152 
00153 NS_IMETHODIMP
00154 nsInputStreamTransport::OpenOutputStream(PRUint32 flags,
00155                                          PRUint32 segsize,
00156                                          PRUint32 segcount,
00157                                          nsIOutputStream **result)
00158 {
00159     // this transport only supports reading!
00160     NS_NOTREACHED("nsInputStreamTransport::OpenOutputStream");
00161     return NS_ERROR_UNEXPECTED;
00162 }
00163 
00164 NS_IMETHODIMP
00165 nsInputStreamTransport::Close(nsresult reason)
00166 {
00167     if (NS_SUCCEEDED(reason))
00168         reason = NS_BASE_STREAM_CLOSED;
00169 
00170     return mPipeIn->CloseWithStatus(reason);
00171 }
00172 
00173 NS_IMETHODIMP
00174 nsInputStreamTransport::SetEventSink(nsITransportEventSink *sink,
00175                                      nsIEventTarget *target)
00176 {
00177     NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
00178 
00179     if (target)
00180         return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
00181                                               sink, target);
00182 
00183     mEventSink = sink;
00184     return NS_OK;
00185 }
00186 
00189 NS_IMETHODIMP
00190 nsInputStreamTransport::Close()
00191 {
00192     if (mCloseWhenDone)
00193         mSource->Close();
00194 
00195     // make additional reads return early...
00196     mOffset = mLimit = 0;
00197     return NS_OK;
00198 }
00199 
00200 NS_IMETHODIMP
00201 nsInputStreamTransport::Available(PRUint32 *result)
00202 {
00203     return NS_ERROR_NOT_IMPLEMENTED;
00204 }
00205 
00206 NS_IMETHODIMP
00207 nsInputStreamTransport::Read(char *buf, PRUint32 count, PRUint32 *result)
00208 {
00209     if (mFirstTime) {
00210         mFirstTime = PR_FALSE;
00211         if (mOffset != nsUint64(0)) {
00212             // read from current position if offset equal to max
00213             if (mOffset != LL_MAXUINT) {
00214                 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
00215                 // Note: The casts to PRUint64 are needed to cast to PRInt64, as
00216                 // nsUint64 can't directly be cast to PRInt64
00217                 if (seekable)
00218                     seekable->Seek(nsISeekableStream::NS_SEEK_SET, PRUint64(mOffset));
00219             }
00220             // reset offset to zero so we can use it to enforce limit
00221             mOffset = 0;
00222         }
00223     }
00224 
00225     // limit amount read
00226     PRUint32 max = mLimit - mOffset;
00227     if (max == 0) {
00228         *result = 0;
00229         return NS_OK;
00230     }
00231         
00232     if (count > max)
00233         count = max;
00234 
00235     nsresult rv = mSource->Read(buf, count, result);
00236 
00237     if (NS_SUCCEEDED(rv)) {
00238         mOffset += *result;
00239         if (mEventSink)
00240             mEventSink->OnTransportStatus(this, STATUS_READING, mOffset, mLimit);
00241     }
00242     return rv;
00243 }
00244 
00245 NS_IMETHODIMP
00246 nsInputStreamTransport::ReadSegments(nsWriteSegmentFun writer, void *closure,
00247                                      PRUint32 count, PRUint32 *result)
00248 {
00249     return NS_ERROR_NOT_IMPLEMENTED;
00250 }
00251 
00252 NS_IMETHODIMP
00253 nsInputStreamTransport::IsNonBlocking(PRBool *result)
00254 {
00255     *result = PR_FALSE;
00256     return NS_OK;
00257 }
00258 
00259 //-----------------------------------------------------------------------------
00260 // nsOutputStreamTransport
00261 //
00262 // Implements nsIOutputStream as a wrapper around the real input stream.  This
00263 // allows the transport to support seeking, range-limiting, progress reporting,
00264 // and close-when-done semantics while utilizing NS_AsyncCopy.
00265 //-----------------------------------------------------------------------------
00266 
00267 class nsOutputStreamTransport : public nsITransport
00268                               , public nsIOutputStream
00269 {
00270 public:
00271     NS_DECL_ISUPPORTS
00272     NS_DECL_NSITRANSPORT
00273     NS_DECL_NSIOUTPUTSTREAM
00274 
00275     nsOutputStreamTransport(nsIOutputStream *sink,
00276                             PRUint64 offset,
00277                             PRUint64 limit,
00278                             PRBool closeWhenDone)
00279         : mSink(sink)
00280         , mOffset(offset)
00281         , mLimit(limit)
00282         , mCloseWhenDone(closeWhenDone)
00283         , mFirstTime(PR_TRUE)
00284         , mInProgress(PR_FALSE)
00285     {
00286     }
00287 
00288     virtual ~nsOutputStreamTransport()
00289     {
00290     }
00291 
00292 private:
00293     nsCOMPtr<nsIAsyncOutputStream>  mPipeOut;
00294  
00295     // while the copy is active, these members may only be accessed from the
00296     // nsIOutputStream implementation.
00297     nsCOMPtr<nsITransportEventSink> mEventSink;
00298     nsCOMPtr<nsIOutputStream>       mSink;
00299     nsUint64                        mOffset;
00300     nsUint64                        mLimit;
00301     PRPackedBool                    mCloseWhenDone;
00302     PRPackedBool                    mFirstTime;
00303 
00304     // this variable serves as a lock to prevent the state of the transport
00305     // from being modified once the copy is in progress.
00306     PRPackedBool                    mInProgress;
00307 };
00308 
00309 NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamTransport,
00310                               nsITransport,
00311                               nsIOutputStream)
00312 
00313 
00315 NS_IMETHODIMP
00316 nsOutputStreamTransport::OpenInputStream(PRUint32 flags,
00317                                          PRUint32 segsize,
00318                                          PRUint32 segcount,
00319                                          nsIInputStream **result)
00320 {
00321     // this transport only supports writing!
00322     NS_NOTREACHED("nsOutputStreamTransport::OpenInputStream");
00323     return NS_ERROR_UNEXPECTED;
00324 }
00325 
00326 NS_IMETHODIMP
00327 nsOutputStreamTransport::OpenOutputStream(PRUint32 flags,
00328                                           PRUint32 segsize,
00329                                           PRUint32 segcount,
00330                                           nsIOutputStream **result)
00331 {
00332     NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
00333 
00334     nsresult rv;
00335     nsCOMPtr<nsIEventTarget> target =
00336             do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv);
00337     if (NS_FAILED(rv)) return rv;
00338 
00339     // XXX if the caller requests an unbuffered stream, then perhaps
00340     //     we'd want to simply return mSink; however, then we would
00341     //     not be writing to mSink on a background thread.  is this ok?
00342  
00343     PRBool nonblocking = !(flags & OPEN_BLOCKING);
00344 
00345     net_ResolveSegmentParams(segsize, segcount);
00346     nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
00347 
00348     nsCOMPtr<nsIAsyncInputStream> pipeIn;
00349     rv = NS_NewPipe2(getter_AddRefs(pipeIn),
00350                      getter_AddRefs(mPipeOut),
00351                      PR_TRUE, nonblocking,
00352                      segsize, segcount, segalloc);
00353     if (NS_FAILED(rv)) return rv;
00354 
00355     mInProgress = PR_TRUE;
00356 
00357     // startup async copy process...
00358     rv = NS_AsyncCopy(pipeIn, this, target,
00359                       NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
00360     if (NS_SUCCEEDED(rv))
00361         NS_ADDREF(*result = mPipeOut);
00362 
00363     return rv;
00364 }
00365 
00366 NS_IMETHODIMP
00367 nsOutputStreamTransport::Close(nsresult reason)
00368 {
00369     if (NS_SUCCEEDED(reason))
00370         reason = NS_BASE_STREAM_CLOSED;
00371 
00372     return mPipeOut->CloseWithStatus(reason);
00373 }
00374 
00375 NS_IMETHODIMP
00376 nsOutputStreamTransport::SetEventSink(nsITransportEventSink *sink,
00377                                       nsIEventTarget *target)
00378 {
00379     NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
00380 
00381     if (target)
00382         return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
00383                                               sink, target);
00384 
00385     mEventSink = sink;
00386     return NS_OK;
00387 }
00388 
00391 NS_IMETHODIMP
00392 nsOutputStreamTransport::Close()
00393 {
00394     if (mCloseWhenDone)
00395         mSink->Close();
00396 
00397     // make additional writes return early...
00398     mOffset = mLimit = 0;
00399     return NS_OK;
00400 }
00401 
00402 NS_IMETHODIMP
00403 nsOutputStreamTransport::Flush()
00404 {
00405     return NS_OK;
00406 }
00407 
00408 NS_IMETHODIMP
00409 nsOutputStreamTransport::Write(const char *buf, PRUint32 count, PRUint32 *result)
00410 {
00411     if (mFirstTime) {
00412         mFirstTime = PR_FALSE;
00413         if (mOffset != nsUint64(0)) {
00414             // write to current position if offset equal to max
00415             if (mOffset != LL_MAXUINT) {
00416                 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
00417                 // Note: The casts to PRUint64 are needed to cast to PRInt64, as
00418                 // nsUint64 can't directly be cast to PRInt64
00419                 if (seekable)
00420                     seekable->Seek(nsISeekableStream::NS_SEEK_SET, PRUint64(mOffset));
00421             }
00422             // reset offset to zero so we can use it to enforce limit
00423             mOffset = 0;
00424         }
00425     }
00426 
00427     // limit amount written
00428     PRUint32 max = mLimit - mOffset;
00429     if (max == 0) {
00430         *result = 0;
00431         return NS_OK;
00432     }
00433         
00434     if (count > max)
00435         count = max;
00436 
00437     nsresult rv = mSink->Write(buf, count, result);
00438 
00439     if (NS_SUCCEEDED(rv)) {
00440         mOffset += *result;
00441         if (mEventSink)
00442             mEventSink->OnTransportStatus(this, STATUS_WRITING, mOffset, mLimit);
00443     }
00444     return rv;
00445 }
00446 
00447 NS_IMETHODIMP
00448 nsOutputStreamTransport::WriteSegments(nsReadSegmentFun reader, void *closure,
00449                                        PRUint32 count, PRUint32 *result)
00450 {
00451     return NS_ERROR_NOT_IMPLEMENTED;
00452 }
00453 
00454 NS_IMETHODIMP
00455 nsOutputStreamTransport::WriteFrom(nsIInputStream *in, PRUint32 count, PRUint32 *result)
00456 {
00457     return NS_ERROR_NOT_IMPLEMENTED;
00458 }
00459 
00460 NS_IMETHODIMP
00461 nsOutputStreamTransport::IsNonBlocking(PRBool *result)
00462 {
00463     *result = PR_FALSE;
00464     return NS_OK;
00465 }
00466 
00467 //-----------------------------------------------------------------------------
00468 // nsStreamTransportService
00469 //-----------------------------------------------------------------------------
00470 
00471 NS_IMPL_THREADSAFE_ISUPPORTS1(nsStreamTransportService, nsIStreamTransportService)
00472 
00473 NS_IMETHODIMP
00474 nsStreamTransportService::CreateInputTransport(nsIInputStream *stream,
00475                                                PRInt64 offset,
00476                                                PRInt64 limit,
00477                                                PRBool closeWhenDone,
00478                                                nsITransport **result)
00479 {
00480     nsInputStreamTransport *trans =
00481         new nsInputStreamTransport(stream, offset, limit, closeWhenDone);
00482     if (!trans)
00483         return NS_ERROR_OUT_OF_MEMORY;
00484     NS_ADDREF(*result = trans);
00485     return NS_OK;
00486 }
00487 
00488 NS_IMETHODIMP
00489 nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream,
00490                                                 PRInt64 offset,
00491                                                 PRInt64 limit,
00492                                                 PRBool closeWhenDone,
00493                                                 nsITransport **result)
00494 {
00495     nsOutputStreamTransport *trans =
00496         new nsOutputStreamTransport(stream, offset, limit, closeWhenDone);
00497     if (!trans)
00498         return NS_ERROR_OUT_OF_MEMORY;
00499     NS_ADDREF(*result = trans);
00500     return NS_OK;
00501 }