2
* The contents of this file are subject to the Mozilla Public
3
* License Version 1.1 (the "MPL"); you may not use this file
4
* except in compliance with the MPL. You may obtain a copy of
5
* the MPL at http://www.mozilla.org/MPL/
7
* Software distributed under the MPL is distributed on an "AS
8
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9
* implied. See the MPL for the specific language governing
10
* rights and limitations under the MPL.
12
* The Original Code is protoZilla.
14
* The Initial Developer of the Original Code is Ramalingam Saravanan.
15
* Portions created by Ramalingam Saravanan <svn@xmlterm.org> are
16
* Copyright (C) 2000 Ramalingam Saravanan. All Rights Reserved.
19
* Patrick Brunschwig <patrick@mozilla-enigmail.org>
21
* Alternatively, the contents of this file may be used under the
22
* terms of the GNU General Public License (the "GPL"), in which case
23
* the provisions of the GPL are applicable instead of
24
* those above. If you wish to allow use of your version of this
25
* file only under the terms of the GPL and not to allow
26
* others to use your version of this file under the MPL, indicate
27
* your decision by deleting the provisions above and replace them
28
* with the notice and other provisions required by the GPL.
29
* If you do not delete the provisions above, a recipient
30
* may use your version of this file under either the MPL or the
34
// Logging of debug output
35
// The following define statement should occur before any include statements
36
#define FORCE_PR_LOG /* Allow logging even in release build */
41
#include "nsAutoLock.h"
42
#include "nsIInputStream.h"
43
#include "nsIThread.h"
44
#include "nsIHttpChannel.h"
45
#include "nsNetUtil.h"
46
#include "nsDirectoryServiceUtils.h"
47
#include "nsDirectoryServiceDefs.h"
50
#ifndef _IPC_FORCE_INTERNAL_API
51
#include "nsStringAPI.h"
56
#include "nsIPCBuffer.h"
59
PRLogModuleInfo* gIPCBufferLog = NULL;
62
#define ERROR_LOG(args) PR_LOG(gIPCBufferLog,PR_LOG_ERROR,args)
63
#define WARNING_LOG(args) PR_LOG(gIPCBufferLog,PR_LOG_WARNING,args)
64
#define DEBUG_LOG(args) PR_LOG(gIPCBufferLog,PR_LOG_DEBUG,args)
66
#define NS_PIPE_CONSOLE_BUFFER_SIZE (1024)
68
static const PRUint32 kCharMax = NS_PIPE_CONSOLE_BUFFER_SIZE;
70
///////////////////////////////////////////////////////////////////////////////
72
// nsIPCBuffer implementation
74
// nsISupports implementation
75
NS_IMPL_THREADSAFE_ISUPPORTS5(nsIPCBuffer,
83
// nsIPCBuffer implementation
84
nsIPCBuffer::nsIPCBuffer()
85
: mFinalized(PR_FALSE),
86
mThreadJoined(PR_FALSE),
87
mOverflowed(PR_FALSE),
88
mOverflowFile(PR_FALSE),
90
mRequestStarted(PR_FALSE),
91
mRequestStopped(PR_FALSE),
100
mPipeWrite(IPC_NULL_HANDLE),
101
mPipeRead(IPC_NULL_HANDLE),
104
mTempOutStream(nsnull),
105
mTempInStream(nsnull),
109
mObserverContext(nsnull)
114
if (gIPCBufferLog == nsnull) {
115
gIPCBufferLog = PR_NewLogModule("nsIPCBuffer");
121
nsCOMPtr<nsIThread> myThread;
122
rv = IPC_GET_THREAD(myThread);
123
DEBUG_LOG(("nsIPCBuffer:: <<<<<<<<< CTOR(%p): myThread=%p\n",
124
this, myThread.get()));
129
nsIPCBuffer::~nsIPCBuffer()
133
nsCOMPtr<nsIThread> myThread;
134
rv = IPC_GET_THREAD(myThread);
135
DEBUG_LOG(("nsIPCBuffer:: >>>>>>>>> DTOR(%p): myThread=%p\n",
136
this, myThread.get()));
142
PR_DestroyLock(mLock);
146
///////////////////////////////////////////////////////////////////////////////
147
// nsIPCBuffer methods:
148
///////////////////////////////////////////////////////////////////////////////
151
nsIPCBuffer::Finalize(PRBool destructor)
153
DEBUG_LOG(("nsIPCBuffer::Finalize: \n"));
158
mFinalized = PR_TRUE;
160
nsCOMPtr<nsIIPCBuffer> self;
162
// Hold a reference to ourselves to prevent our DTOR from being called
163
// while finalizing. Automatically released upon returning.
169
IPC_Close(mPipeWrite);
170
mPipeWrite = IPC_NULL_HANDLE;
173
// Release owning refs
174
mPipeThread = nsnull;
176
mObserverContext = nsnull;
189
DEBUG_LOG(("nsIPCBuffer::Init: \n"));
191
if (mLock == nsnull) {
192
mLock = PR_NewLock();
194
return NS_ERROR_OUT_OF_MEMORY;
201
nsIPCBuffer::Open(PRUint32 maxBytes, PRBool overflowFile)
205
DEBUG_LOG(("nsIPCBuffer::Open: %d, %d\n", maxBytes, (int) overflowFile));
207
NS_ENSURE_SUCCESS(rv, rv);
209
if (maxBytes == -1) {
210
mMaxBytes = PR_INT32_MAX;
213
mMaxBytes = maxBytes;
215
mOverflowFile = overflowFile;
222
nsIPCBuffer::OpenURI(nsIURI* aURI, PRInt32 maxBytes, PRBool synchronous,
223
nsIRequestObserver* observer, nsISupports* context)
225
DEBUG_LOG(("nsIPCBuffer::OpenURI: \n"));
230
NS_ENSURE_SUCCESS(rv, rv);
232
mMaxBytes = maxBytes;
234
mObserver = observer;
235
mObserverContext = context;
237
nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
238
NS_ENSURE_SUCCESS(rv, rv);
240
nsCOMPtr<nsIChannel> channel;
241
rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(channel));
242
NS_ENSURE_SUCCESS(rv, rv);
244
nsCOMPtr<nsISupports> ctxt = do_QueryInterface(aURI);
247
// Initiate asynchronous loading of URI
248
rv = channel->AsyncOpen( (nsIStreamListener*) this, ctxt );
249
NS_ENSURE_SUCCESS(rv, rv);
251
DEBUG_LOG(("nsIPCBuffer::OpenURI: Starting asynchronous load ...\n"));
255
// Synchronous loading (DOESN'T USUALLY WORK!!!)
256
DEBUG_LOG(("nsIPCBuffer::OpenURI: Starting synchronous load ...\n"));
257
nsCOMPtr<nsIInputStream> inputStream;
258
rv = channel->Open(getter_AddRefs(inputStream));
259
NS_ENSURE_SUCCESS(rv, rv);
261
OnStartRequest(nsnull, mObserverContext);
267
// Read and append output until end-of-file
268
rv = inputStream->Read((char *) buf, 1024, &readCount);
269
NS_ENSURE_SUCCESS(rv, rv);
271
if (!readCount) break;
273
rv = WriteBuf(buf, readCount);
274
NS_ENSURE_SUCCESS(rv, rv);
277
// Close input stream
278
inputStream->Close();
280
OnStopRequest(nsnull, mObserverContext, 0);
287
nsIPCBuffer::GetStopped(PRBool* _retval)
289
NS_ENSURE_ARG(_retval);
290
*_retval = mRequestStopped;
296
nsIPCBuffer::GetTotalBytes(PRUint32* _retval)
298
NS_ENSURE_ARG(_retval);
299
*_retval = mByteCount;
305
nsIPCBuffer::OpenInputStream(nsIInputStream** result)
309
DEBUG_LOG(("nsIPCBuffer::OpenInputStream: \n"));
311
if (!mRequestStopped) {
312
ERROR_LOG(("nsIPCBuffer::OpenInputStream: ERROR - request not stopped\n"));
313
return NS_ERROR_NOT_INITIALIZED;
318
if (mByteCount && mTempFile) {
319
rv = OpenTempInStream();
320
NS_ENSURE_SUCCESS(rv, rv);
323
return this->QueryInterface(NS_GET_IID(nsIInputStream), (void**)result);
327
#define SAFE_TMP_FILENAME "nsenig.tmp"
330
nsIPCBuffer::CreateTempFile()
334
DEBUG_LOG(("nsIPCBuffer::CreateTempFile: \n"));
337
return NS_ERROR_FAILURE;
339
nsCOMPtr<nsIProperties> directoryService =
340
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
341
directoryService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(mTempFile));
344
return NS_ERROR_OUT_OF_MEMORY;
346
mTempFile->AppendNative(nsDependentCString(SAFE_TMP_FILENAME));
347
if (NS_FAILED(mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600))) {
348
return NS_ERROR_FAILURE;
351
nsCAutoString nativePath;
352
mTempFile->GetNativePath(nativePath);
354
DEBUG_LOG(("nsIPCBuffer::CreateTempFile: %s\n",
357
mTempOutStream = do_CreateInstance("@mozilla.org/network/file-output-stream;1", &rv);
358
NS_ENSURE_SUCCESS(rv, rv);
360
rv = mTempOutStream->Init(mTempFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 00600, 0);
366
nsIPCBuffer::WriteTempOutStream(const char* buf, PRUint32 count)
369
return NS_ERROR_FAILURE;
375
nsresult rv = mTempOutStream->Write(buf, count, &writeCount);
377
if (writeCount != count)
378
return NS_ERROR_FAILURE;
384
nsIPCBuffer::CloseTempOutStream()
388
DEBUG_LOG(("nsIPCBuffer::CloseTempOutStream: \n"));
390
if (mTempOutStream) {
391
if (NS_FAILED(mTempOutStream->Flush())) {
392
rv = NS_ERROR_FAILURE;
395
if (NS_FAILED(mTempOutStream->Close())) {
396
rv = NS_ERROR_FAILURE;
398
mTempOutStream = nsnull;
405
nsIPCBuffer::OpenTempInStream()
409
DEBUG_LOG(("nsIPCBuffer::OpenTempInStream: \n"));
412
return NS_ERROR_FAILURE;
414
if (mTempOutStream) {
415
ERROR_LOG(("nsIPCBuffer::OpenTempInStream: ERROR - TempOutStream still open!\n"));
416
return NS_ERROR_FAILURE;
419
mTempInStream = do_CreateInstance("@mozilla.org/network/file-input-stream;1", &rv);
420
NS_ENSURE_SUCCESS(rv, rv);
422
rv = mTempInStream->Init(mTempFile, PR_RDONLY, 00600, 0);
428
nsIPCBuffer::CloseTempInStream()
430
DEBUG_LOG(("nsIPCBuffer::CloseTempInStream: \n"));
434
rv = mTempInStream->Close();
435
//delete mTempInStream;
436
mTempInStream = nsnull;
445
nsIPCBuffer::RemoveTempFile()
447
DEBUG_LOG(("nsIPCBuffer::RemoveTempFile: \n"));
449
if (mTempOutStream) {
450
// Close overflow file
451
CloseTempOutStream();
455
// Close overflow file
461
nsCAutoString nativePath;
462
mTempFile->GetNativePath(nativePath);
463
DEBUG_LOG(("nsIPCBuffer::RemoveTempFile: Removing %s\n",
466
if (NS_FAILED(mTempFile->Remove(PR_FALSE))) {
467
return NS_ERROR_FAILURE;
478
nsIPCBuffer::GetData(char** _retval)
480
nsAutoLock lock(mLock);
483
return NS_ERROR_NULL_POINTER;
485
// Copy portion of console data to be returned
486
nsCAutoString bufCopy (mByteBuf);
488
// Replace any NULs with '0'
489
PRInt32 nulIndex = 0;
490
while (nulIndex != -1) {
491
nulIndex = bufCopy.FindChar(char(0));
492
if (nulIndex != -1) {
493
bufCopy.Replace(nulIndex, 1, "0", 1);
497
// Duplicate new C string
498
*_retval = ToNewCString(bufCopy);
500
return NS_ERROR_OUT_OF_MEMORY;
506
///////////////////////////////////////////////////////////////////////////////
507
// nsIPipeListener methods (thread-safe)
508
///////////////////////////////////////////////////////////////////////////////
511
nsIPCBuffer::Observe(nsIRequestObserver* observer, nsISupports* context)
513
nsAutoLock lock(mLock);
514
DEBUG_LOG(("nsIPCBuffer::Observe: %p, %p\n", observer, context));
516
mObserver = observer;
517
mObserverContext = context;
524
nsIPCBuffer::GetJoinable(PRBool *_retval)
526
DEBUG_LOG(("nsIPCBuffer::GetJoinable: 1\n"));
535
nsIPCBuffer::Shutdown()
537
nsAutoLock lock(mLock);
538
DEBUG_LOG(("nsIPCBuffer::Shutdown:\n"));
548
nsIPCBuffer::GetByteData(PRUint32 *count, char **data)
550
nsAutoLock lock(mLock);
552
DEBUG_LOG(("nsIPCBuffer::GetByteData:\n"));
555
return NS_ERROR_NULL_POINTER;
558
*count = mByteBuf.Length();
559
*data = reinterpret_cast<char*>(nsMemory::Alloc((*count)+1));
561
return NS_ERROR_OUT_OF_MEMORY;
563
memcpy(*data, mByteBuf.get(), *count);
565
// NUL terminate byte array (just to be safe!)
566
(*data)[*count] = '\0';
573
nsIPCBuffer::GetOverflowed(PRBool *_retval)
575
nsAutoLock lock(mLock);
577
DEBUG_LOG(("nsIPCBuffer::GetOverflowed: %d\n", (int) mOverflowed));
579
*_retval = mOverflowed;
586
nsIPCBuffer::Write(const char* str)
588
// Note: Locking occurs in WriteBuf
590
DEBUG_LOG(("nsIPCBuffer::Write: %s\n", str));
592
PRUint32 len = strlen(str);
596
return WriteBuf(str, len);
601
nsIPCBuffer::WriteBuf(const char* buf, PRUint32 count)
604
nsAutoLock lock(mLock);
606
DEBUG_LOG(("nsIPCBuffer::WriteBuf: %d (%d)\n", count, mByteCount));
617
rv = WriteTempOutStream(buf, count);
622
// Find space available in buffer
623
PRInt32 nAvail = mMaxBytes - mByteBuf.Length();
625
if (nAvail >= (int) count) {
626
mByteBuf.Append(buf, count);
631
mByteBuf.Append(buf, nAvail);
634
mOverflowed = PR_TRUE;
635
DEBUG_LOG(("nsIPCBuffer::WriteBuf: buffer overflow\n"));
642
// Write out previously buffered data first
643
rv = WriteTempOutStream(mByteBuf.get(), mByteBuf.Length());
644
NS_ENSURE_SUCCESS(rv, rv);
646
rv = WriteTempOutStream(buf+nAvail, count-nAvail);
656
// Nested lock to avoid deadlock while waiting for Join
657
nsAutoLock lock(mLock);
658
DEBUG_LOG(("nsIPCBuffer::Join:\n"));
660
if (mThreadJoined || !mPipeThread)
664
// Close write pipe before joining
665
IPC_Close(mPipeWrite);
666
mPipeWrite = IPC_NULL_HANDLE;
670
rv = mPipeThread->Shutdown();
672
NS_ENSURE_SUCCESS(rv, rv);
674
mThreadJoined = PR_TRUE;
680
nsIPCBuffer::GetFileDesc(IPCFileDesc* *_retval)
684
nsAutoLock lock(mLock);
686
DEBUG_LOG(("nsIPCBuffer::GetFileDesc:\n"));
689
return NS_ERROR_NULL_POINTER;
691
if (!mFinalized && !mPipeThread) {
693
PRStatus status = IPC_CreateInheritablePipe(&mPipeRead, &mPipeWrite,
695
if (status != PR_SUCCESS) {
696
ERROR_LOG(("nsIPCBuffer::GetFileDesc: IPC_CreateInheritablePipe failed\n"));
697
return NS_ERROR_FAILURE;
700
// Spin up a new thread to handle STDOUT polling
701
rv = NS_NewThread(getter_AddRefs(mPipeThread), this);
703
NS_ENSURE_SUCCESS(rv, rv);
706
if (mPipeWrite == IPC_NULL_HANDLE)
707
return NS_ERROR_FAILURE;
709
*_retval = mPipeWrite;
713
///////////////////////////////////////////////////////////////////////////////
714
// nsIRequestObserver methods
715
///////////////////////////////////////////////////////////////////////////////
718
nsIPCBuffer::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
720
DEBUG_LOG(("nsIPCBuffer::OnStartRequest:\n"));
722
nsCOMPtr<nsIRequestObserver> observer;
723
nsCOMPtr<nsISupports> observerContext;
725
nsAutoLock lock(mLock);
727
mRequestStarted = PR_TRUE;
732
observer = mObserver;
733
observerContext = mObserverContext;
736
return observer->OnStartRequest(aRequest, observerContext);
740
nsIPCBuffer::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
743
DEBUG_LOG(("nsIPCBuffer::OnStopRequest:\n"));
745
nsCOMPtr<nsIRequestObserver> observer;
746
nsCOMPtr<nsISupports> observerContext;
748
nsAutoLock lock(mLock);
750
mRequestStopped = PR_TRUE;
751
CloseTempOutStream();
756
observer = mObserver;
757
observerContext = mObserverContext;
760
return observer->OnStopRequest(aRequest, observerContext, aStatus);
763
///////////////////////////////////////////////////////////////////////////////
764
// nsIStreamListener method
765
///////////////////////////////////////////////////////////////////////////////
768
nsIPCBuffer::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
769
nsIInputStream *aInputStream,
770
PRUint32 aSourceOffset,
775
DEBUG_LOG(("nsIPCBuffer::OnDataAVailable: %d\n", aLength));
778
PRUint32 readCount, readMax;
780
while (aLength > 0) {
781
readMax = (aLength < kCharMax) ? aLength : kCharMax;
782
rv = aInputStream->Read((char *) buf, readMax, &readCount);
784
ERROR_LOG(("nsIPCBuffer::OnDataAvailable: Error in reading from input stream, %x\n", rv));
788
if (readCount <= 0) return NS_OK;
790
rv = WriteBuf(buf, readCount);
791
if (NS_FAILED(rv)) return rv;
793
aLength -= readCount;
799
///////////////////////////////////////////////////////////////////////////////
800
// nsIRunnable methods:
801
// (runs as a new thread)
802
///////////////////////////////////////////////////////////////////////////////
810
nsCOMPtr<nsIThread> myThread;
811
rv = IPC_GET_THREAD(myThread);
812
DEBUG_LOG(("nsIPCBuffer::Run: myThread=%p\n", myThread.get()));
820
// Read data from pipe (blocking)
821
readCount = IPC_Read(mPipeRead, (char *) buf, kCharMax);
823
DEBUG_LOG(("nsIPCBuffer::Run: Read %d chars\n", readCount));
830
if (readCount < (int) kCharMax) {
831
buf[readCount] = '\0';
832
DEBUG_LOG(("nsIPCBuffer::Run: buf='%s'\n", buf));
836
// Append data read to console
837
WriteBuf(buf, readCount);
840
// Clear any NSPR interrupt
844
IPC_Close(mPipeRead);
845
mPipeRead = IPC_NULL_HANDLE;
850
///////////////////////////////////////////////////////////////////////////////
851
// nsIInputStream methods
852
///////////////////////////////////////////////////////////////////////////////
855
nsIPCBuffer::Available(PRUint32* _retval)
858
return NS_ERROR_NULL_POINTER;
860
*_retval = (mByteCount > mStreamOffset) ?
861
mByteCount - mStreamOffset : 0;
863
DEBUG_LOG(("nsIPCBuffer::Available: %d (%d)\n", *_retval, mByteCount));
869
nsIPCBuffer::Read(char* buf, PRUint32 count,
872
DEBUG_LOG(("nsIPCBuffer::Read: %d\n", count));
876
if (!buf || !readCount)
877
return NS_ERROR_NULL_POINTER;
879
PRInt32 avail = (mByteCount > mStreamOffset) ?
880
mByteCount - mStreamOffset : 0;
882
PRUint32 readyCount = ((PRUint32) avail > count) ? count : avail;
886
rv = mTempInStream->Read((char *)buf, readyCount, readCount);
887
NS_ENSURE_SUCCESS(rv, rv);
889
memcpy(buf, mByteBuf.get()+mStreamOffset, readyCount);
890
*readCount = readyCount;
894
mStreamOffset += *readCount;
896
if (mStreamOffset >= mByteCount) {
904
nsIPCBuffer::ReadSegments(nsWriteSegmentFun writer,
905
void * aClosure, PRUint32 count,
909
DEBUG_LOG(("nsIPCBuffer::ReadSegments: %d\n", count));
912
return NS_ERROR_NULL_POINTER;
914
PRUint32 avail, readyCount, writeCount;
917
if (!mTempInStream) {
919
while ((count > 0) && (mStreamOffset < mByteCount)) {
920
avail = mByteCount - mStreamOffset;
921
readyCount = ((PRUint32) avail > count) ? count : avail;
923
rv = writer((nsIInputStream*)(this),
924
aClosure, mByteBuf.get()+mStreamOffset,
925
mStreamOffset, readyCount, &writeCount);
926
NS_ENSURE_SUCCESS(rv, rv);
929
return (NS_ERROR_FAILURE);
931
DEBUG_LOG(("nsIPCBuffer::ReadSegments: writer %d\n", writeCount));
933
*readCount += writeCount;
934
mStreamOffset += writeCount;
941
while ((count > 0) && (mStreamOffset < mByteCount)) {
942
avail = (count < kCharMax) ? count : kCharMax;
943
rv = mTempInStream->Read((char *) buf, avail, &readyCount);
944
NS_ENSURE_SUCCESS(rv, rv);
947
ERROR_LOG(("nsIPCBuffer::ReadSegments: Error in reading from TempInputStream\n"));
948
return NS_ERROR_FAILURE;
951
rv = writer((nsIInputStream*)(this),
953
mStreamOffset, readyCount, &writeCount);
954
NS_ENSURE_SUCCESS(rv, rv);
956
return NS_ERROR_FAILURE;
958
DEBUG_LOG(("nsIPCBuffer::ReadSegments: writer %d (Temp)\n", writeCount));
960
*readCount += writeCount;
961
mStreamOffset += writeCount;
966
if (mStreamOffset >= mByteCount) {
975
nsIPCBuffer::IsNonBlocking(PRBool *aNonBlocking)
977
DEBUG_LOG(("nsIPCBuffer::IsNonBlocking: \n"));
979
*aNonBlocking = (mTempInStream == nsnull);
986
DEBUG_LOG(("nsIPCBuffer::Close: \n"));