Back to index

lightning-sunbird  0.9+nobinonly
nsSocketTransport2.cpp
Go to the documentation of this file.
00001 /* vim:set ts=4 sw=4 et cindent: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@netscape.com>
00024  *   Malcolm Smith <malsmith@cs.rmit.edu.au>
00025  *   Andreas Otte <andreas.otte@debitel.net>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #ifdef MOZ_LOGGING
00042 #define FORCE_PR_LOG
00043 #endif
00044 
00045 #include "nsSocketTransport2.h"
00046 #include "nsIOService.h"
00047 #include "nsStreamUtils.h"
00048 #include "nsNetSegmentUtils.h"
00049 #include "nsTransportUtils.h"
00050 #include "nsProxyInfo.h"
00051 #include "nsNetCID.h"
00052 #include "nsAutoLock.h"
00053 #include "nsCOMPtr.h"
00054 #include "netCore.h"
00055 #include "nsInt64.h"
00056 #include "prmem.h"
00057 #include "pratom.h"
00058 #include "plstr.h"
00059 #include "prnetdb.h"
00060 #include "prerror.h"
00061 #include "prerr.h"
00062 
00063 #include "nsIServiceManager.h"
00064 #include "nsIProxyObjectManager.h"
00065 #include "nsISocketProviderService.h"
00066 #include "nsISocketProvider.h"
00067 #include "nsISSLSocketControl.h"
00068 #include "nsIPipe.h"
00069 
00070 #if defined(XP_WIN)
00071 #include "nsNativeConnectionHelper.h"
00072 #endif
00073 
00074 //-----------------------------------------------------------------------------
00075 
00076 static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
00077 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
00078 
00079 //-----------------------------------------------------------------------------
00080 
00081 class nsSocketEvent : public PLEvent
00082 {
00083 public:
00084     nsSocketEvent(nsSocketTransport *transport, PRUint32 type,
00085                   nsresult status = NS_OK, nsISupports *param = nsnull)
00086         : mType(type)
00087         , mStatus(status)
00088         , mParam(param)
00089     {
00090         NS_ADDREF(transport);
00091         PL_InitEvent(this, transport, HandleEvent, DestroyEvent);
00092     }
00093 
00094     PR_STATIC_CALLBACK(void*)
00095     HandleEvent(PLEvent *event)
00096     {
00097         nsSocketTransport *trans = (nsSocketTransport *) event->owner; 
00098         nsSocketEvent *se = (nsSocketEvent *) event;
00099         trans->OnSocketEvent(se->mType, se->mStatus, se->mParam);
00100         return nsnull;
00101     }
00102 
00103     PR_STATIC_CALLBACK(void)
00104     DestroyEvent(PLEvent *event)
00105     {
00106         nsSocketTransport *trans = (nsSocketTransport *) event->owner; 
00107         NS_RELEASE(trans);
00108         delete (nsSocketEvent *) event;
00109     }
00110 
00111 private:
00112     PRUint32              mType;
00113     nsresult              mStatus;
00114     nsCOMPtr<nsISupports> mParam;
00115 };
00116 
00117 //-----------------------------------------------------------------------------
00118 
00119 //#define TEST_CONNECT_ERRORS
00120 #ifdef TEST_CONNECT_ERRORS
00121 #include <stdlib.h>
00122 static PRErrorCode RandomizeConnectError(PRErrorCode code)
00123 {
00124     //
00125     // To test out these errors, load http://www.yahoo.com/.  It should load
00126     // correctly despite the random occurance of these errors.
00127     //
00128     int n = rand();
00129     if (n > RAND_MAX/2) {
00130         struct {
00131             PRErrorCode err_code;
00132             const char *err_name;
00133         } 
00134         errors[] = {
00135             //
00136             // These errors should be recoverable provided there is another
00137             // IP address in mDNSRecord.
00138             //
00139             { PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR" },
00140             { PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR" },
00141             //
00142             // This error will cause this socket transport to error out;
00143             // however, if the consumer is HTTP, then the HTTP transaction
00144             // should be restarted when this error occurs.
00145             //
00146             { PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR" },
00147         };
00148         n = n % (sizeof(errors)/sizeof(errors[0]));
00149         code = errors[n].err_code;
00150         LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
00151     }
00152     return code;
00153 }
00154 #endif
00155 
00156 //-----------------------------------------------------------------------------
00157 
00158 static nsresult
00159 ErrorAccordingToNSPR(PRErrorCode errorCode)
00160 {
00161     nsresult rv;
00162     switch (errorCode) {
00163     case PR_WOULD_BLOCK_ERROR:
00164         rv = NS_BASE_STREAM_WOULD_BLOCK;
00165         break;
00166     case PR_CONNECT_ABORTED_ERROR:
00167     case PR_CONNECT_RESET_ERROR:
00168         rv = NS_ERROR_NET_RESET;
00169         break;
00170     case PR_END_OF_FILE_ERROR: // XXX document this correlation
00171         rv = NS_ERROR_NET_INTERRUPT;
00172         break;
00173     case PR_CONNECT_REFUSED_ERROR:
00174     case PR_NETWORK_UNREACHABLE_ERROR: // XXX need new nsresult for this!
00175     case PR_HOST_UNREACHABLE_ERROR:    // XXX and this!
00176     case PR_ADDRESS_NOT_AVAILABLE_ERROR:
00177     // Treat EACCES as a soft error since (at least on Linux) connect() returns
00178     // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
00179     case PR_ADDRESS_NOT_SUPPORTED_ERROR:
00180     case PR_NO_ACCESS_RIGHTS_ERROR:
00181         rv = NS_ERROR_CONNECTION_REFUSED;
00182         break;
00183     case PR_IO_TIMEOUT_ERROR:
00184     case PR_CONNECT_TIMEOUT_ERROR:
00185         rv = NS_ERROR_NET_TIMEOUT;
00186         break;
00187     default:
00188         rv = NS_ERROR_FAILURE;
00189     }
00190     LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
00191     return rv;
00192 }
00193 
00194 //-----------------------------------------------------------------------------
00195 // socket input stream impl 
00196 //-----------------------------------------------------------------------------
00197 
00198 nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
00199     : mTransport(trans)
00200     , mReaderRefCnt(0)
00201     , mCondition(NS_OK)
00202     , mCallbackFlags(0)
00203     , mByteCount(0)
00204 {
00205 }
00206 
00207 nsSocketInputStream::~nsSocketInputStream()
00208 {
00209 }
00210 
00211 // called on the socket transport thread...
00212 //
00213 //   condition : failure code if socket has been closed
00214 //
00215 void
00216 nsSocketInputStream::OnSocketReady(nsresult condition)
00217 {
00218     LOG(("nsSocketInputStream::OnSocketReady [this=%x cond=%x]\n",
00219         this, condition));
00220 
00221     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00222 
00223     nsCOMPtr<nsIInputStreamCallback> callback;
00224     {
00225         nsAutoLock lock(mTransport->mLock);
00226 
00227         // update condition, but be careful not to erase an already
00228         // existing error condition.
00229         if (NS_SUCCEEDED(mCondition))
00230             mCondition = condition;
00231 
00232         // ignore event if only waiting for closure and not closed.
00233         if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
00234             callback = mCallback;
00235             mCallback = nsnull;
00236             mCallbackFlags = 0;
00237         }
00238     }
00239 
00240     if (callback)
00241         callback->OnInputStreamReady(this);
00242 }
00243 
00244 NS_IMPL_QUERY_INTERFACE2(nsSocketInputStream,
00245                          nsIInputStream,
00246                          nsIAsyncInputStream)
00247 
00248 NS_IMETHODIMP_(nsrefcnt)
00249 nsSocketInputStream::AddRef()
00250 {
00251     PR_AtomicIncrement((PRInt32*)&mReaderRefCnt);
00252     return mTransport->AddRef();
00253 }
00254 
00255 NS_IMETHODIMP_(nsrefcnt)
00256 nsSocketInputStream::Release()
00257 {
00258     if (PR_AtomicDecrement((PRInt32*)&mReaderRefCnt) == 0)
00259         Close();
00260     return mTransport->Release();
00261 }
00262 
00263 NS_IMETHODIMP
00264 nsSocketInputStream::Close()
00265 {
00266     return CloseWithStatus(NS_BASE_STREAM_CLOSED);
00267 }
00268 
00269 NS_IMETHODIMP
00270 nsSocketInputStream::Available(PRUint32 *avail)
00271 {
00272     LOG(("nsSocketInputStream::Available [this=%x]\n", this));
00273 
00274     *avail = 0;
00275 
00276     PRFileDesc *fd;
00277     {
00278         nsAutoLock lock(mTransport->mLock);
00279 
00280         if (NS_FAILED(mCondition))
00281             return mCondition;
00282 
00283         fd = mTransport->GetFD_Locked();
00284         if (!fd)
00285             return NS_BASE_STREAM_WOULD_BLOCK;
00286     }
00287 
00288     // cannot hold lock while calling NSPR.  (worried about the fact that PSM
00289     // synchronously proxies notifications over to the UI thread, which could
00290     // mistakenly try to re-enter this code.)
00291     PRInt32 n = PR_Available(fd);
00292 
00293     nsresult rv;
00294     {
00295         nsAutoLock lock(mTransport->mLock);
00296 
00297         mTransport->ReleaseFD_Locked(fd);
00298 
00299         if (n >= 0)
00300             *avail = n;
00301         else {
00302             PRErrorCode code = PR_GetError();
00303             if (code == PR_WOULD_BLOCK_ERROR)
00304                 return NS_BASE_STREAM_WOULD_BLOCK;
00305             mCondition = ErrorAccordingToNSPR(code);
00306         }
00307         rv = mCondition;
00308     }
00309     if (NS_FAILED(rv))
00310         mTransport->OnInputClosed(rv);
00311     return rv;
00312 }
00313 
00314 NS_IMETHODIMP
00315 nsSocketInputStream::Read(char *buf, PRUint32 count, PRUint32 *countRead)
00316 {
00317     LOG(("nsSocketInputStream::Read [this=%x count=%u]\n", this, count));
00318 
00319     *countRead = 0;
00320 
00321     PRFileDesc *fd;
00322     {
00323         nsAutoLock lock(mTransport->mLock);
00324 
00325         if (NS_FAILED(mCondition))
00326             return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
00327 
00328         fd = mTransport->GetFD_Locked();
00329         if (!fd)
00330             return NS_BASE_STREAM_WOULD_BLOCK;
00331     }
00332 
00333     LOG(("  calling PR_Read [count=%u]\n", count));
00334 
00335     // cannot hold lock while calling NSPR.  (worried about the fact that PSM
00336     // synchronously proxies notifications over to the UI thread, which could
00337     // mistakenly try to re-enter this code.)
00338     PRInt32 n = PR_Read(fd, buf, count);
00339 
00340     LOG(("  PR_Read returned [n=%d]\n", n));
00341 
00342     nsresult rv;
00343     {
00344         nsAutoLock lock(mTransport->mLock);
00345 
00346 #ifdef ENABLE_SOCKET_TRACING
00347         if (n > 0)
00348             mTransport->TraceInBuf(buf, n);
00349 #endif
00350 
00351         mTransport->ReleaseFD_Locked(fd);
00352 
00353         if (n > 0)
00354             mByteCount += (*countRead = n);
00355         else if (n < 0) {
00356             PRErrorCode code = PR_GetError();
00357             if (code == PR_WOULD_BLOCK_ERROR)
00358                 return NS_BASE_STREAM_WOULD_BLOCK;
00359             mCondition = ErrorAccordingToNSPR(code);
00360         }
00361         rv = mCondition;
00362     }
00363     if (NS_FAILED(rv))
00364         mTransport->OnInputClosed(rv);
00365 
00366     // only send this notification if we have indeed read some data.
00367     // see bug 196827 for an example of why this is important.
00368     if (n > 0)
00369         mTransport->SendStatus(nsISocketTransport::STATUS_RECEIVING_FROM);
00370     return rv;
00371 }
00372 
00373 NS_IMETHODIMP
00374 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
00375                                   PRUint32 count, PRUint32 *countRead)
00376 {
00377     // socket stream is unbuffered
00378     return NS_ERROR_NOT_IMPLEMENTED;
00379 }
00380 
00381 NS_IMETHODIMP
00382 nsSocketInputStream::IsNonBlocking(PRBool *nonblocking)
00383 {
00384     *nonblocking = PR_TRUE;
00385     return NS_OK;
00386 }
00387 
00388 NS_IMETHODIMP
00389 nsSocketInputStream::CloseWithStatus(nsresult reason)
00390 {
00391     LOG(("nsSocketInputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason));
00392 
00393     // may be called from any thread
00394  
00395     nsresult rv;
00396     {
00397         nsAutoLock lock(mTransport->mLock);
00398 
00399         if (NS_SUCCEEDED(mCondition))
00400             rv = mCondition = reason;
00401         else
00402             rv = NS_OK;
00403     }
00404     if (NS_FAILED(rv))
00405         mTransport->OnInputClosed(rv);
00406     return NS_OK;
00407 }
00408 
00409 NS_IMETHODIMP
00410 nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
00411                                PRUint32 flags,
00412                                PRUint32 amount,
00413                                nsIEventTarget *target)
00414 {
00415     LOG(("nsSocketInputStream::AsyncWait [this=%x]\n", this));
00416 
00417     {
00418         nsAutoLock lock(mTransport->mLock);
00419 
00420         if (target) {
00421             //
00422             // build event proxy
00423             //
00424             // failure to create an event proxy (most likely out of memory)
00425             // shouldn't alter the state of the transport.
00426             //
00427             nsCOMPtr<nsIInputStreamCallback> temp;
00428             nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(temp),
00429                                                       callback, target);
00430             if (NS_FAILED(rv)) return rv;
00431             mCallback = temp;
00432         }
00433         else
00434             mCallback = callback;
00435 
00436         mCallbackFlags = flags;
00437     }
00438     mTransport->OnInputPending();
00439     return NS_OK;
00440 }
00441 
00442 //-----------------------------------------------------------------------------
00443 // socket output stream impl 
00444 //-----------------------------------------------------------------------------
00445 
00446 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
00447     : mTransport(trans)
00448     , mWriterRefCnt(0)
00449     , mCondition(NS_OK)
00450     , mCallbackFlags(0)
00451     , mByteCount(0)
00452 {
00453 }
00454 
00455 nsSocketOutputStream::~nsSocketOutputStream()
00456 {
00457 }
00458 
00459 // called on the socket transport thread...
00460 //
00461 //   condition : failure code if socket has been closed
00462 //
00463 void
00464 nsSocketOutputStream::OnSocketReady(nsresult condition)
00465 {
00466     LOG(("nsSocketOutputStream::OnSocketReady [this=%x cond=%x]\n",
00467         this, condition));
00468 
00469     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00470 
00471     nsCOMPtr<nsIOutputStreamCallback> callback;
00472     {
00473         nsAutoLock lock(mTransport->mLock);
00474 
00475         // update condition, but be careful not to erase an already
00476         // existing error condition.
00477         if (NS_SUCCEEDED(mCondition))
00478             mCondition = condition;
00479 
00480         // ignore event if only waiting for closure and not closed.
00481         if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
00482             callback = mCallback;
00483             mCallback = nsnull;
00484             mCallbackFlags = 0;
00485         }
00486     }
00487 
00488     if (callback)
00489         callback->OnOutputStreamReady(this);
00490 }
00491 
00492 NS_IMPL_QUERY_INTERFACE2(nsSocketOutputStream,
00493                          nsIOutputStream,
00494                          nsIAsyncOutputStream)
00495 
00496 NS_IMETHODIMP_(nsrefcnt)
00497 nsSocketOutputStream::AddRef()
00498 {
00499     PR_AtomicIncrement((PRInt32*)&mWriterRefCnt);
00500     return mTransport->AddRef();
00501 }
00502 
00503 NS_IMETHODIMP_(nsrefcnt)
00504 nsSocketOutputStream::Release()
00505 {
00506     if (PR_AtomicDecrement((PRInt32*)&mWriterRefCnt) == 0)
00507         Close();
00508     return mTransport->Release();
00509 }
00510 
00511 NS_IMETHODIMP
00512 nsSocketOutputStream::Close()
00513 {
00514     return CloseWithStatus(NS_BASE_STREAM_CLOSED);
00515 }
00516 
00517 NS_IMETHODIMP
00518 nsSocketOutputStream::Flush()
00519 {
00520     return NS_OK;
00521 }
00522 
00523 NS_IMETHODIMP
00524 nsSocketOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *countWritten)
00525 {
00526     LOG(("nsSocketOutputStream::Write [this=%x count=%u]\n", this, count));
00527 
00528     *countWritten = 0;
00529 
00530     if (count == 0)
00531         return NS_OK;
00532 
00533     PRFileDesc *fd;
00534     {
00535         nsAutoLock lock(mTransport->mLock);
00536 
00537         if (NS_FAILED(mCondition))
00538             return mCondition;
00539         
00540         fd = mTransport->GetFD_Locked();
00541         if (!fd)
00542             return NS_BASE_STREAM_WOULD_BLOCK;
00543     }
00544 
00545     LOG(("  calling PR_Write [count=%u]\n", count));
00546 
00547     // cannot hold lock while calling NSPR.  (worried about the fact that PSM
00548     // synchronously proxies notifications over to the UI thread, which could
00549     // mistakenly try to re-enter this code.)
00550     PRInt32 n = PR_Write(fd, buf, count);
00551 
00552     LOG(("  PR_Write returned [n=%d]\n", n));
00553     NS_ASSERTION(n != 0, "unexpected return value");
00554 
00555     nsresult rv;
00556     {
00557         nsAutoLock lock(mTransport->mLock);
00558 
00559 #ifdef ENABLE_SOCKET_TRACING
00560     if (n > 0)
00561         mTransport->TraceOutBuf(buf, n);
00562 #endif
00563 
00564         mTransport->ReleaseFD_Locked(fd);
00565 
00566         if (n > 0)
00567             mByteCount += (*countWritten = n);
00568         else if (n < 0) {
00569             PRErrorCode code = PR_GetError();
00570             if (code == PR_WOULD_BLOCK_ERROR)
00571                 return NS_BASE_STREAM_WOULD_BLOCK;
00572             mCondition = ErrorAccordingToNSPR(code);
00573         }
00574         rv = mCondition;
00575     }
00576     if (NS_FAILED(rv))
00577         mTransport->OnOutputClosed(rv);
00578 
00579     // only send this notification if we have indeed written some data.
00580     // see bug 196827 for an example of why this is important.
00581     if (n > 0)
00582         mTransport->SendStatus(nsISocketTransport::STATUS_SENDING_TO);
00583     return rv;
00584 }
00585 
00586 NS_IMETHODIMP
00587 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
00588                                     PRUint32 count, PRUint32 *countRead)
00589 {
00590     // socket stream is unbuffered
00591     return NS_ERROR_NOT_IMPLEMENTED;
00592 }
00593 
00594 NS_METHOD
00595 nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
00596                                         void *closure,
00597                                         const char *fromSegment,
00598                                         PRUint32 offset,
00599                                         PRUint32 count,
00600                                         PRUint32 *countRead)
00601 {
00602     nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
00603     return self->Write(fromSegment, count, countRead);
00604 }
00605 
00606 NS_IMETHODIMP
00607 nsSocketOutputStream::WriteFrom(nsIInputStream *stream, PRUint32 count, PRUint32 *countRead)
00608 {
00609     return stream->ReadSegments(WriteFromSegments, this, count, countRead);
00610 }
00611 
00612 NS_IMETHODIMP
00613 nsSocketOutputStream::IsNonBlocking(PRBool *nonblocking)
00614 {
00615     *nonblocking = PR_TRUE;
00616     return NS_OK;
00617 }
00618 
00619 NS_IMETHODIMP
00620 nsSocketOutputStream::CloseWithStatus(nsresult reason)
00621 {
00622     LOG(("nsSocketOutputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason));
00623 
00624     // may be called from any thread
00625  
00626     nsresult rv;
00627     {
00628         nsAutoLock lock(mTransport->mLock);
00629 
00630         if (NS_SUCCEEDED(mCondition))
00631             rv = mCondition = reason;
00632         else
00633             rv = NS_OK;
00634     }
00635     if (NS_FAILED(rv))
00636         mTransport->OnOutputClosed(rv);
00637     return NS_OK;
00638 }
00639 
00640 NS_IMETHODIMP
00641 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
00642                                 PRUint32 flags,
00643                                 PRUint32 amount,
00644                                 nsIEventTarget *target)
00645 {
00646     LOG(("nsSocketOutputStream::AsyncWait [this=%x]\n", this));
00647 
00648     {
00649         nsAutoLock lock(mTransport->mLock);
00650 
00651         if (target) {
00652             //
00653             // build event proxy
00654             //
00655             // failure to create an event proxy (most likely out of memory)
00656             // shouldn't alter the state of the transport.
00657             //
00658             nsCOMPtr<nsIOutputStreamCallback> temp;
00659             nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(temp),
00660                                                        callback, target);
00661             if (NS_FAILED(rv)) return rv;
00662             mCallback = temp;
00663         }
00664         else
00665             mCallback = callback;
00666 
00667         mCallbackFlags = flags;
00668     }
00669     mTransport->OnOutputPending();
00670     return NS_OK;
00671 }
00672 
00673 //-----------------------------------------------------------------------------
00674 // socket transport impl
00675 //-----------------------------------------------------------------------------
00676 
00677 nsSocketTransport::nsSocketTransport()
00678     : mTypes(nsnull)
00679     , mTypeCount(0)
00680     , mPort(0)
00681     , mProxyPort(0)
00682     , mProxyTransparent(PR_FALSE)
00683     , mProxyTransparentResolvesHost(PR_FALSE)
00684     , mState(STATE_CLOSED)
00685     , mAttached(PR_FALSE)
00686     , mInputClosed(PR_TRUE)
00687     , mOutputClosed(PR_TRUE)
00688     , mResolving(PR_FALSE)
00689     , mLock(PR_NewLock())
00690     , mFD(nsnull)
00691     , mFDref(0)
00692     , mFDconnected(PR_FALSE)
00693     , mInput(this)
00694     , mOutput(this)
00695 {
00696     LOG(("creating nsSocketTransport @%x\n", this));
00697 
00698     NS_ADDREF(gSocketTransportService);
00699 
00700     mTimeouts[TIMEOUT_CONNECT]    = PR_UINT16_MAX; // no timeout
00701     mTimeouts[TIMEOUT_READ_WRITE] = PR_UINT16_MAX; // no timeout
00702 }
00703 
00704 nsSocketTransport::~nsSocketTransport()
00705 {
00706     LOG(("destroying nsSocketTransport @%x\n", this));
00707 
00708     // cleanup socket type info
00709     if (mTypes) {
00710         PRUint32 i;
00711         for (i=0; i<mTypeCount; ++i)
00712             PL_strfree(mTypes[i]);
00713         free(mTypes);
00714     }
00715 
00716     if (mLock)
00717         PR_DestroyLock(mLock);
00718  
00719     nsSocketTransportService *serv = gSocketTransportService;
00720     NS_RELEASE(serv); // nulls argument
00721 }
00722 
00723 nsresult
00724 nsSocketTransport::Init(const char **types, PRUint32 typeCount,
00725                         const nsACString &host, PRUint16 port,
00726                         nsIProxyInfo *givenProxyInfo)
00727 {
00728     if (!mLock)
00729         return NS_ERROR_OUT_OF_MEMORY;
00730 
00731     nsCOMPtr<nsProxyInfo> proxyInfo;
00732     if (givenProxyInfo) {
00733         proxyInfo = do_QueryInterface(givenProxyInfo);
00734         NS_ENSURE_ARG(proxyInfo);
00735     }
00736 
00737     // init socket type info
00738 
00739     mPort = port;
00740     mHost = host;
00741 
00742     const char *proxyType = nsnull;
00743     if (proxyInfo) {
00744         mProxyPort = proxyInfo->Port();
00745         mProxyHost = proxyInfo->Host();
00746         // grab proxy type (looking for "socks" for example)
00747         proxyType = proxyInfo->Type();
00748         if (proxyType && (strcmp(proxyType, "http") == 0 ||
00749                           strcmp(proxyType, "direct") == 0 ||
00750                           strcmp(proxyType, "unknown") == 0))
00751             proxyType = nsnull;
00752     }
00753 
00754     LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s:%hu]\n",
00755         this, mHost.get(), mPort, mProxyHost.get(), mProxyPort));
00756 
00757     // include proxy type as a socket type if proxy type is not "http"
00758     mTypeCount = typeCount + (proxyType != nsnull);
00759     if (!mTypeCount)
00760         return NS_OK;
00761 
00762     // if we have socket types, then the socket provider service had
00763     // better exist!
00764     nsresult rv;
00765     nsCOMPtr<nsISocketProviderService> spserv =
00766         do_GetService(kSocketProviderServiceCID, &rv);
00767     if (NS_FAILED(rv)) return rv;
00768 
00769     mTypes = (char **) malloc(mTypeCount * sizeof(char *));
00770     if (!mTypes)
00771         return NS_ERROR_OUT_OF_MEMORY;
00772 
00773     // now verify that each socket type has a registered socket provider.
00774     for (PRUint32 i = 0, type = 0; i < mTypeCount; ++i) {
00775         // store socket types
00776         if (i == 0 && proxyType)
00777             mTypes[i] = PL_strdup(proxyType);
00778         else
00779             mTypes[i] = PL_strdup(types[type++]);
00780 
00781         if (!mTypes[i]) {
00782             mTypeCount = i;
00783             return NS_ERROR_OUT_OF_MEMORY;
00784         }
00785         nsCOMPtr<nsISocketProvider> provider;
00786         rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
00787         if (NS_FAILED(rv)) {
00788             NS_WARNING("no registered socket provider");
00789             return rv;
00790         }
00791 
00792         // note if socket type corresponds to a transparent proxy
00793         // XXX don't hardcode SOCKS here (use proxy info's flags instead).
00794         if ((strcmp(mTypes[i], "socks") == 0) ||
00795             (strcmp(mTypes[i], "socks4") == 0)) {
00796             mProxyTransparent = PR_TRUE;
00797 
00798             if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
00799                 // we want the SOCKS layer to send the hostname
00800                 // and port to the proxy and let it do the DNS.
00801                 mProxyTransparentResolvesHost = PR_TRUE;
00802             }
00803         }
00804     }
00805 
00806     return NS_OK;
00807 }
00808 
00809 nsresult
00810 nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const PRNetAddr *addr)
00811 {
00812     if (!mLock)
00813         return NS_ERROR_OUT_OF_MEMORY;
00814 
00815     NS_ASSERTION(!mFD, "already initialized");
00816 
00817     char buf[64];
00818     PR_NetAddrToString(addr, buf, sizeof(buf));
00819     mHost.Assign(buf);
00820 
00821     PRUint16 port;
00822     if (addr->raw.family == PR_AF_INET)
00823         port = addr->inet.port;
00824     else
00825         port = addr->ipv6.port;
00826     mPort = PR_ntohs(port);
00827 
00828     memcpy(&mNetAddr, addr, sizeof(PRNetAddr));
00829 
00830     mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
00831     mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
00832     mState = STATE_TRANSFERRING;
00833 
00834     mFD = fd;
00835     mFDref = 1;
00836     mFDconnected = 1;
00837 
00838     // make sure new socket is non-blocking
00839     PRSocketOptionData opt;
00840     opt.option = PR_SockOpt_Nonblocking;
00841     opt.value.non_blocking = PR_TRUE;
00842     PR_SetSocketOption(mFD, &opt);
00843 
00844     LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
00845         this, mHost.get(), mPort));
00846 
00847     // jump to InitiateSocket to get ourselves attached to the STS poll list.
00848     return PostEvent(MSG_RETRY_INIT_SOCKET);
00849 }
00850 
00851 nsresult
00852 nsSocketTransport::PostEvent(PRUint32 type, nsresult status, nsISupports *param)
00853 {
00854     LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
00855         this, type, status, param));
00856 
00857     PLEvent *event = new nsSocketEvent(this, type, status, param);
00858     if (!event)
00859         return NS_ERROR_OUT_OF_MEMORY;
00860 
00861     nsresult rv = gSocketTransportService->PostEvent(event);
00862     if (NS_FAILED(rv))
00863         PL_DestroyEvent(event);
00864 
00865     return rv;
00866 }
00867 
00868 void
00869 nsSocketTransport::SendStatus(nsresult status)
00870 {
00871     LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status));
00872 
00873     nsCOMPtr<nsITransportEventSink> sink;
00874     PRUint64 progress;
00875     {
00876         nsAutoLock lock(mLock);
00877         sink = mEventSink;
00878         switch (status) {
00879         case STATUS_SENDING_TO:
00880             progress = mOutput.ByteCount();
00881             break;
00882         case STATUS_RECEIVING_FROM:
00883             progress = mInput.ByteCount();
00884             break;
00885         default:
00886             progress = 0;
00887             break;
00888         }
00889     }
00890     if (sink)
00891         sink->OnTransportStatus(this, status, progress, LL_MAXUINT);
00892 }
00893 
00894 nsresult
00895 nsSocketTransport::ResolveHost()
00896 {
00897     LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
00898 
00899     nsresult rv;
00900 
00901     if (!mProxyHost.IsEmpty()) {
00902         if (!mProxyTransparent || mProxyTransparentResolvesHost) {
00903             // When not resolving mHost locally, we still want to ensure that
00904             // it only contains valid characters.  See bug 304904 for details.
00905             if (!net_IsValidHostName(mHost))
00906                 return NS_ERROR_UNKNOWN_HOST;
00907         }
00908         if (mProxyTransparentResolvesHost) {
00909             // Name resolution is done on the server side.  Just pretend
00910             // client resolution is complete, this will get picked up later.
00911             // since we don't need to do DNS now, we bypass the resolving
00912             // step by initializing mNetAddr to an empty address, but we
00913             // must keep the port. The SOCKS IO layer will use the hostname
00914             // we send it when it's created, rather than the empty address
00915             // we send with the connect call.
00916             mState = STATE_RESOLVING;
00917             PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, SocketPort(), &mNetAddr);
00918             return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nsnull);
00919         }
00920     }
00921 
00922     nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
00923     if (NS_FAILED(rv)) return rv;
00924 
00925     mResolving = PR_TRUE;
00926 
00927     rv = dns->AsyncResolve(SocketHost(), 0, this, nsnull,
00928                            getter_AddRefs(mDNSRequest));
00929     if (NS_SUCCEEDED(rv)) {
00930         LOG(("  advancing to STATE_RESOLVING\n"));
00931         mState = STATE_RESOLVING;
00932         // only report that we are resolving if we are still resolving...
00933         if (mResolving)
00934             SendStatus(STATUS_RESOLVING);
00935     }
00936     return rv;
00937 }
00938 
00939 nsresult
00940 nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool &usingSSL)
00941 {
00942     LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
00943 
00944     nsresult rv;
00945 
00946     proxyTransparent = PR_FALSE;
00947     usingSSL = PR_FALSE;
00948 
00949     if (mTypeCount == 0) {
00950         fd = PR_OpenTCPSocket(mNetAddr.raw.family);
00951         rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00952     }
00953     else {
00954         fd = nsnull;
00955 
00956         nsCOMPtr<nsISocketProviderService> spserv =
00957             do_GetService(kSocketProviderServiceCID, &rv);
00958         if (NS_FAILED(rv)) return rv;
00959 
00960         const char *host       = mHost.get();
00961         PRInt32     port       = (PRInt32) mPort;
00962         const char *proxyHost  = mProxyHost.IsEmpty() ? nsnull : mProxyHost.get();
00963         PRInt32     proxyPort  = (PRInt32) mProxyPort;
00964         PRUint32    proxyFlags = 0;
00965 
00966         PRUint32 i;
00967         for (i=0; i<mTypeCount; ++i) {
00968             nsCOMPtr<nsISocketProvider> provider;
00969 
00970             LOG(("  pushing io layer [%u:%s]\n", i, mTypes[i]));
00971 
00972             rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
00973             if (NS_FAILED(rv))
00974                 break;
00975 
00976             if (mProxyTransparentResolvesHost)
00977                 proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
00978 
00979             nsCOMPtr<nsISupports> secinfo;
00980             if (i == 0) {
00981                 // if this is the first type, we'll want the 
00982                 // service to allocate a new socket
00983                 rv = provider->NewSocket(mNetAddr.raw.family,
00984                                          host, port, proxyHost, proxyPort,
00985                                          proxyFlags, &fd,
00986                                          getter_AddRefs(secinfo));
00987 
00988                 if (NS_SUCCEEDED(rv) && !fd) {
00989                     NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
00990                     rv = NS_ERROR_UNEXPECTED;
00991                 }
00992             }
00993             else {
00994                 // the socket has already been allocated, 
00995                 // so we just want the service to add itself
00996                 // to the stack (such as pushing an io layer)
00997                 rv = provider->AddToSocket(mNetAddr.raw.family,
00998                                            host, port, proxyHost, proxyPort,
00999                                            proxyFlags, fd,
01000                                            getter_AddRefs(secinfo));
01001             }
01002             proxyFlags = 0;
01003             if (NS_FAILED(rv))
01004                 break;
01005 
01006             // if the service was ssl or starttls, we want to hold onto the socket info
01007             PRBool isSSL = (strcmp(mTypes[i], "ssl") == 0);
01008             if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
01009                 // remember security info and give notification callbacks to PSM...
01010                 nsCOMPtr<nsIInterfaceRequestor> callbacks;
01011                 {
01012                     nsAutoLock lock(mLock);
01013                     mSecInfo = secinfo;
01014                     callbacks = mCallbacks;
01015                     LOG(("  [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
01016                 }
01017                 // don't call into PSM while holding mLock!!
01018                 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
01019                 if (secCtrl)
01020                     secCtrl->SetNotificationCallbacks(callbacks);
01021                 // remember if socket type is SSL so we can ProxyStartSSL if need be.
01022                 usingSSL = isSSL;
01023             }
01024             else if ((strcmp(mTypes[i], "socks") == 0) ||
01025                      (strcmp(mTypes[i], "socks4") == 0)) {
01026                 // since socks is transparent, any layers above
01027                 // it do not have to worry about proxy stuff
01028                 proxyHost = nsnull;
01029                 proxyPort = -1;
01030                 proxyTransparent = PR_TRUE;
01031             }
01032         }
01033 
01034         if (NS_FAILED(rv)) {
01035             LOG(("  error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
01036             if (fd)
01037                 PR_Close(fd);
01038         }
01039     }
01040 
01041     return rv;
01042 }
01043 
01044 nsresult
01045 nsSocketTransport::InitiateSocket()
01046 {
01047     LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
01048 
01049     nsresult rv;
01050 
01051     //
01052     // find out if it is going to be ok to attach another socket to the STS.
01053     // if not then we have to wait for the STS to tell us that it is ok.
01054     // the notification is asynchronous, which means that when we could be
01055     // in a race to call AttachSocket once notified.  for this reason, when
01056     // we get notified, we just re-enter this function.  as a result, we are
01057     // sure to ask again before calling AttachSocket.  in this way we deal
01058     // with the race condition.  though it isn't the most elegant solution,
01059     // it is far simpler than trying to build a system that would guarantee
01060     // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
01061     // 194402 for more info.
01062     //
01063     if (!gSocketTransportService->CanAttachSocket()) {
01064         PLEvent *event = new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
01065         if (!event)
01066             return NS_ERROR_OUT_OF_MEMORY;
01067         rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
01068         if (NS_FAILED(rv))
01069             PL_DestroyEvent(event);
01070         return rv;
01071     }
01072 
01073     //
01074     // if we already have a connected socket, then just attach and return.
01075     //
01076     if (mFD) {
01077         rv = gSocketTransportService->AttachSocket(mFD, this);
01078         if (NS_SUCCEEDED(rv))
01079             mAttached = PR_TRUE;
01080         return rv;
01081     }
01082 
01083     //
01084     // create new socket fd, push io layers, etc.
01085     //
01086     PRFileDesc *fd;
01087     PRBool proxyTransparent;
01088     PRBool usingSSL;
01089 
01090     rv = BuildSocket(fd, proxyTransparent, usingSSL);
01091     if (NS_FAILED(rv)) {
01092         LOG(("  BuildSocket failed [rv=%x]\n", rv));
01093         return rv;
01094     }
01095 
01096     PRStatus status;
01097 
01098     // Make the socket non-blocking...
01099     PRSocketOptionData opt;
01100     opt.option = PR_SockOpt_Nonblocking;
01101     opt.value.non_blocking = PR_TRUE;
01102     status = PR_SetSocketOption(fd, &opt);
01103     NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
01104     
01105     // inform socket transport about this newly created socket...
01106     rv = gSocketTransportService->AttachSocket(fd, this);
01107     if (NS_FAILED(rv)) {
01108         PR_Close(fd);
01109         return rv;
01110     }
01111     mAttached = PR_TRUE;
01112 
01113     // assign mFD so that we can properly handle OnSocketDetached before we've
01114     // established a connection.
01115     {
01116         nsAutoLock lock(mLock);
01117         mFD = fd;
01118         mFDref = 1;
01119         mFDconnected = PR_FALSE;
01120     }
01121 
01122     LOG(("  advancing to STATE_CONNECTING\n"));
01123     mState = STATE_CONNECTING;
01124     mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
01125     SendStatus(STATUS_CONNECTING_TO);
01126 
01127 #if defined(PR_LOGGING)
01128     if (LOG_ENABLED()) {
01129         char buf[64];
01130         PR_NetAddrToString(&mNetAddr, buf, sizeof(buf));
01131         LOG(("  trying address: %s\n", buf));
01132     }
01133 #endif
01134 
01135     // 
01136     // Initiate the connect() to the host...  
01137     //
01138     status = PR_Connect(fd, &mNetAddr, NS_SOCKET_CONNECT_TIMEOUT);
01139     if (status == PR_SUCCESS) {
01140         // 
01141         // we are connected!
01142         //
01143         OnSocketConnected();
01144     }
01145     else {
01146         PRErrorCode code = PR_GetError();
01147 #if defined(TEST_CONNECT_ERRORS)
01148         code = RandomizeConnectError(code);
01149 #endif
01150         //
01151         // If the PR_Connect(...) would block, then poll for a connection.
01152         //
01153         if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
01154             mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
01155         //
01156         // If the socket is already connected, then return success...
01157         //
01158         else if (PR_IS_CONNECTED_ERROR == code) {
01159             //
01160             // we are connected!
01161             //
01162             OnSocketConnected();
01163 
01164             if (mSecInfo && !mProxyHost.IsEmpty() && proxyTransparent && usingSSL) {
01165                 // if the connection phase is finished, and the ssl layer has
01166                 // been pushed, and we were proxying (transparently; ie. nothing
01167                 // has to happen in the protocol layer above us), it's time for
01168                 // the ssl to start doing it's thing.
01169                 nsCOMPtr<nsISSLSocketControl> secCtrl =
01170                     do_QueryInterface(mSecInfo);
01171                 if (secCtrl) {
01172                     LOG(("  calling ProxyStartSSL()\n"));
01173                     secCtrl->ProxyStartSSL();
01174                 }
01175                 // XXX what if we were forced to poll on the socket for a successful
01176                 // connection... wouldn't we need to call ProxyStartSSL after a call
01177                 // to PR_ConnectContinue indicates that we are connected?
01178                 //
01179                 // XXX this appears to be what the old socket transport did.  why
01180                 // isn't this broken?
01181             }
01182         }
01183         //
01184         // The connection was refused...
01185         //
01186         else {
01187             rv = ErrorAccordingToNSPR(code);
01188             if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
01189                 rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
01190         }
01191     }
01192     return rv;
01193 }
01194 
01195 PRBool
01196 nsSocketTransport::RecoverFromError()
01197 {
01198     NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
01199 
01200     LOG(("nsSocketTransport::RecoverFromError [this=%x state=%x cond=%x]\n",
01201         this, mState, mCondition));
01202 
01203     // can only recover from errors in these states
01204     if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
01205         return PR_FALSE;
01206 
01207     // OK to check this outside mLock
01208     NS_ASSERTION(!mFDconnected, "socket should not be connected");
01209 
01210     // can only recover from these errors
01211     if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
01212         mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
01213         mCondition != NS_ERROR_NET_TIMEOUT &&
01214         mCondition != NS_ERROR_UNKNOWN_HOST &&
01215         mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
01216         return PR_FALSE;
01217 
01218     PRBool tryAgain = PR_FALSE;
01219 
01220     // try next ip address only if past the resolver stage...
01221     if (mState == STATE_CONNECTING && mDNSRecord) {
01222         nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
01223         if (NS_SUCCEEDED(rv)) {
01224             LOG(("  trying again with next ip address\n"));
01225             tryAgain = PR_TRUE;
01226         }
01227     }
01228 
01229 #if defined(XP_WIN)
01230     // If not trying next address, try to make a connection using dialup. 
01231     // Retry if that connection is made.
01232     if (!tryAgain) {
01233         PRBool autodialEnabled;
01234         gSocketTransportService->GetAutodialEnabled(&autodialEnabled);
01235         if (autodialEnabled)
01236             tryAgain = nsNativeConnectionHelper::OnConnectionFailed(SocketHost().get());
01237     }
01238 #endif
01239 
01240     // prepare to try again.
01241     if (tryAgain) {
01242         nsresult rv;
01243         PRUint32 msg;
01244 
01245         if (mState == STATE_CONNECTING) {
01246             mState = STATE_RESOLVING;
01247             msg = MSG_DNS_LOOKUP_COMPLETE;
01248         }
01249         else {
01250             mState = STATE_CLOSED;
01251             msg = MSG_ENSURE_CONNECT;
01252         }
01253 
01254         rv = PostEvent(msg, NS_OK);
01255         if (NS_FAILED(rv))
01256             tryAgain = PR_FALSE;
01257     }
01258 
01259     return tryAgain;
01260 }
01261 
01262 // called on the socket thread only
01263 void
01264 nsSocketTransport::OnMsgInputClosed(nsresult reason)
01265 {
01266     LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
01267         this, reason));
01268 
01269     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
01270 
01271     mInputClosed = PR_TRUE;
01272     // check if event should affect entire transport
01273     if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
01274         mCondition = reason;                // XXX except if NS_FAILED(mCondition), right??
01275     else if (mOutputClosed)
01276         mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
01277     else {
01278         if (mState == STATE_TRANSFERRING)
01279             mPollFlags &= ~PR_POLL_READ;
01280         mInput.OnSocketReady(reason);
01281     }
01282 }
01283 
01284 // called on the socket thread only
01285 void
01286 nsSocketTransport::OnMsgOutputClosed(nsresult reason)
01287 {
01288     LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
01289         this, reason));
01290 
01291     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
01292 
01293     mOutputClosed = PR_TRUE;
01294     // check if event should affect entire transport
01295     if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
01296         mCondition = reason;                // XXX except if NS_FAILED(mCondition), right??
01297     else if (mInputClosed)
01298         mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
01299     else {
01300         if (mState == STATE_TRANSFERRING)
01301             mPollFlags &= ~PR_POLL_WRITE;
01302         mOutput.OnSocketReady(reason);
01303     }
01304 }
01305 
01306 void
01307 nsSocketTransport::OnSocketConnected()
01308 {
01309     LOG(("  advancing to STATE_TRANSFERRING\n"));
01310 
01311     mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
01312     mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
01313     mState = STATE_TRANSFERRING;
01314 
01315     SendStatus(STATUS_CONNECTED_TO);
01316 
01317     // assign mFD (must do this within the transport lock), but take care not
01318     // to trample over mFDref if mFD is already set.
01319     {
01320         nsAutoLock lock(mLock);
01321         NS_ASSERTION(mFD, "no socket");
01322         NS_ASSERTION(mFDref == 1, "wrong socket ref count");
01323         mFDconnected = PR_TRUE;
01324     }
01325 }
01326 
01327 PRFileDesc *
01328 nsSocketTransport::GetFD_Locked()
01329 {
01330     // mFD is not available to the streams while disconnected.
01331     if (!mFDconnected)
01332         return nsnull;
01333 
01334     if (mFD)
01335         mFDref++;
01336 
01337     return mFD;
01338 }
01339 
01340 void
01341 nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
01342 {
01343     NS_ASSERTION(mFD == fd, "wrong fd");
01344 
01345     if (--mFDref == 0) {
01346         LOG(("nsSocketTransport: calling PR_Close [this=%x]\n", this));
01347         PR_Close(mFD);
01348         mFD = nsnull;
01349     }
01350 }
01351 
01352 //-----------------------------------------------------------------------------
01353 // socket event handler impl
01354 
01355 void
01356 nsSocketTransport::OnSocketEvent(PRUint32 type, nsresult status, nsISupports *param)
01357 {
01358     LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
01359         this, type, status, param));
01360 
01361     if (NS_FAILED(mCondition)) {
01362         // block event since we're apparently already dead.
01363         LOG(("  blocking event [condition=%x]\n", mCondition));
01364         //
01365         // notify input/output streams in case either has a pending notify.
01366         //
01367         mInput.OnSocketReady(mCondition);
01368         mOutput.OnSocketReady(mCondition);
01369         return;
01370     }
01371 
01372     switch (type) {
01373     case MSG_ENSURE_CONNECT:
01374         LOG(("  MSG_ENSURE_CONNECT\n"));
01375         //
01376         // ensure that we have created a socket, attached it, and have a
01377         // connection.
01378         //
01379         if (mState == STATE_CLOSED)
01380             mCondition = ResolveHost();
01381         else
01382             LOG(("  ignoring redundant event\n"));
01383         break;
01384 
01385     case MSG_DNS_LOOKUP_COMPLETE:
01386         LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
01387         mDNSRequest = 0;
01388         if (param) {
01389             mDNSRecord = NS_STATIC_CAST(nsIDNSRecord *, param);
01390             mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
01391         }
01392         // status contains DNS lookup status
01393         if (NS_FAILED(status)) {
01394             // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP 
01395             // proxy host is not found, so we fixup the error code.
01396             // For SOCKS proxies (mProxyTransparent == true), the socket 
01397             // transport resolves the real host here, so there's no fixup 
01398             // (see bug 226943).
01399             if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyTransparent &&
01400                 !mProxyHost.IsEmpty())
01401                 mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
01402             else
01403                 mCondition = status;
01404         }
01405         else if (mState == STATE_RESOLVING)
01406             mCondition = InitiateSocket();
01407         break;
01408 
01409     case MSG_RETRY_INIT_SOCKET:
01410         mCondition = InitiateSocket();
01411         break;
01412 
01413     case MSG_INPUT_CLOSED:
01414         LOG(("  MSG_INPUT_CLOSED\n"));
01415         OnMsgInputClosed(status);
01416         break;
01417 
01418     case MSG_INPUT_PENDING:
01419         LOG(("  MSG_INPUT_PENDING\n"));
01420         OnMsgInputPending();
01421         break;
01422 
01423     case MSG_OUTPUT_CLOSED:
01424         LOG(("  MSG_OUTPUT_CLOSED\n"));
01425         OnMsgOutputClosed(status);
01426         break;
01427 
01428     case MSG_OUTPUT_PENDING:
01429         LOG(("  MSG_OUTPUT_PENDING\n"));
01430         OnMsgOutputPending();
01431         break;
01432     case MSG_TIMEOUT_CHANGED:
01433         LOG(("  MSG_TIMEOUT_CHANGED\n"));
01434         mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
01435           ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
01436         break;
01437     default:
01438         LOG(("  unhandled event!\n"));
01439     }
01440     
01441     if (NS_FAILED(mCondition)) {
01442         LOG(("  after event [this=%x cond=%x]\n", this, mCondition));
01443         if (!mAttached) // need to process this error ourselves...
01444             OnSocketDetached(nsnull);
01445     }
01446     else if (mPollFlags == PR_POLL_EXCEPT)
01447         mPollFlags = 0; // make idle
01448 }
01449 
01450 //-----------------------------------------------------------------------------
01451 // socket handler impl
01452 
01453 void
01454 nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
01455 {
01456     LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
01457         this, outFlags));
01458 
01459     if (outFlags == -1) {
01460         LOG(("socket timeout expired\n"));
01461         mCondition = NS_ERROR_NET_TIMEOUT;
01462         return;
01463     }
01464 
01465     if (mState == STATE_TRANSFERRING) {
01466         // if waiting to write and socket is writable or hit an exception.
01467         if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
01468             // assume that we won't need to poll any longer (the stream will
01469             // request that we poll again if it is still pending).
01470             mPollFlags &= ~PR_POLL_WRITE;
01471             mOutput.OnSocketReady(NS_OK);
01472         }
01473         // if waiting to read and socket is readable or hit an exception.
01474         if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
01475             // assume that we won't need to poll any longer (the stream will
01476             // request that we poll again if it is still pending).
01477             mPollFlags &= ~PR_POLL_READ;
01478             mInput.OnSocketReady(NS_OK);
01479         }
01480         // Update poll timeout in case it was changed
01481         mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
01482     }
01483     else if (mState == STATE_CONNECTING) {
01484         PRStatus status = PR_ConnectContinue(fd, outFlags);
01485         if (status == PR_SUCCESS) {
01486             //
01487             // we are connected!
01488             //
01489             OnSocketConnected();
01490         }
01491         else {
01492             PRErrorCode code = PR_GetError();
01493 #if defined(TEST_CONNECT_ERRORS)
01494             code = RandomizeConnectError(code);
01495 #endif
01496             //
01497             // If the connect is still not ready, then continue polling...
01498             //
01499             if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
01500                 // Set up the select flags for connect...
01501                 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
01502                 // Update poll timeout in case it was changed
01503                 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
01504             } 
01505             else {
01506                 //
01507                 // else, the connection failed...
01508                 //
01509                 mCondition = ErrorAccordingToNSPR(code);
01510                 if ((mCondition == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
01511                     mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
01512                 LOG(("  connection failed! [reason=%x]\n", mCondition));
01513             }
01514         }
01515     }
01516     else {
01517         NS_ERROR("unexpected socket state");
01518         mCondition = NS_ERROR_UNEXPECTED;
01519     }
01520 
01521     if (mPollFlags == PR_POLL_EXCEPT)
01522         mPollFlags = 0; // make idle
01523 }
01524 
01525 // called on the socket thread only
01526 void
01527 nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
01528 {
01529     LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
01530         this, mCondition));
01531 
01532     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
01533 
01534     // if we didn't initiate this detach, then be sure to pass an error
01535     // condition up to our consumers.  (e.g., STS is shutting down.)
01536     if (NS_SUCCEEDED(mCondition))
01537         mCondition = NS_ERROR_ABORT;
01538 
01539     if (RecoverFromError())
01540         mCondition = NS_OK;
01541     else {
01542         mState = STATE_CLOSED;
01543 
01544         // make sure there isn't any pending DNS request
01545         if (mDNSRequest) {
01546             mDNSRequest->Cancel(NS_ERROR_ABORT);
01547             mDNSRequest = 0;
01548         }
01549 
01550         //
01551         // notify input/output streams
01552         //
01553         mInput.OnSocketReady(mCondition);
01554         mOutput.OnSocketReady(mCondition);
01555     }
01556 
01557     // break any potential reference cycle between the security info object
01558     // and ourselves by resetting its notification callbacks object.  see
01559     // bug 285991 for details.
01560     nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
01561     if (secCtrl)
01562         secCtrl->SetNotificationCallbacks(nsnull);
01563 
01564     // finally, release our reference to the socket (must do this within
01565     // the transport lock) possibly closing the socket.
01566     {
01567         nsAutoLock lock(mLock);
01568         if (mFD) {
01569             ReleaseFD_Locked(mFD);
01570             // flag mFD as unusable; this prevents other consumers from 
01571             // acquiring a reference to mFD.
01572             mFDconnected = PR_FALSE;
01573         }
01574     }
01575 }
01576 
01577 //-----------------------------------------------------------------------------
01578 // xpcom api
01579 
01580 NS_IMPL_THREADSAFE_ISUPPORTS3(nsSocketTransport,
01581                               nsISocketTransport,
01582                               nsITransport,
01583                               nsIDNSListener)
01584 
01585 NS_IMETHODIMP
01586 nsSocketTransport::OpenInputStream(PRUint32 flags,
01587                                    PRUint32 segsize,
01588                                    PRUint32 segcount,
01589                                    nsIInputStream **result)
01590 {
01591     LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
01592         this, flags));
01593 
01594     NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
01595 
01596     nsresult rv;
01597     nsCOMPtr<nsIAsyncInputStream> pipeIn;
01598 
01599     if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
01600         // XXX if the caller wants blocking, then the caller also gets buffered!
01601         //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
01602         PRBool openBlocking =  (flags & OPEN_BLOCKING);
01603 
01604         net_ResolveSegmentParams(segsize, segcount);
01605         nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
01606 
01607         // create a pipe
01608         nsCOMPtr<nsIAsyncOutputStream> pipeOut;
01609         rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
01610                          !openBlocking, PR_TRUE, segsize, segcount, segalloc);
01611         if (NS_FAILED(rv)) return rv;
01612 
01613         // async copy from socket to pipe
01614         rv = NS_AsyncCopy(&mInput, pipeOut, gSocketTransportService,
01615                           NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
01616         if (NS_FAILED(rv)) return rv;
01617 
01618         *result = pipeIn;
01619     }
01620     else
01621         *result = &mInput;
01622 
01623     // flag input stream as open
01624     mInputClosed = PR_FALSE;
01625 
01626     rv = PostEvent(MSG_ENSURE_CONNECT);
01627     if (NS_FAILED(rv)) return rv;
01628 
01629     NS_ADDREF(*result);
01630     return NS_OK;
01631 }
01632 
01633 NS_IMETHODIMP
01634 nsSocketTransport::OpenOutputStream(PRUint32 flags,
01635                                     PRUint32 segsize,
01636                                     PRUint32 segcount,
01637                                     nsIOutputStream **result)
01638 {
01639     LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
01640         this, flags));
01641 
01642     NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
01643 
01644     nsresult rv;
01645     nsCOMPtr<nsIAsyncOutputStream> pipeOut;
01646     if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
01647         // XXX if the caller wants blocking, then the caller also gets buffered!
01648         //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
01649         PRBool openBlocking =  (flags & OPEN_BLOCKING);
01650 
01651         net_ResolveSegmentParams(segsize, segcount);
01652         nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
01653 
01654         // create a pipe
01655         nsCOMPtr<nsIAsyncInputStream> pipeIn;
01656         rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
01657                          PR_TRUE, !openBlocking, segsize, segcount, segalloc);
01658         if (NS_FAILED(rv)) return rv;
01659 
01660         // async copy from socket to pipe
01661         rv = NS_AsyncCopy(pipeIn, &mOutput, gSocketTransportService,
01662                           NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
01663         if (NS_FAILED(rv)) return rv;
01664 
01665         *result = pipeOut;
01666     }
01667     else
01668         *result = &mOutput;
01669 
01670     // flag output stream as open
01671     mOutputClosed = PR_FALSE;
01672 
01673     rv = PostEvent(MSG_ENSURE_CONNECT);
01674     if (NS_FAILED(rv)) return rv;
01675 
01676     NS_ADDREF(*result);
01677     return NS_OK;
01678 }
01679 
01680 NS_IMETHODIMP
01681 nsSocketTransport::Close(nsresult reason)
01682 {
01683     if (NS_SUCCEEDED(reason))
01684         reason = NS_BASE_STREAM_CLOSED;
01685 
01686     mInput.CloseWithStatus(reason);
01687     mOutput.CloseWithStatus(reason);
01688     return NS_OK;
01689 }
01690 
01691 NS_IMETHODIMP
01692 nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
01693 {
01694     nsAutoLock lock(mLock);
01695     NS_IF_ADDREF(*secinfo = mSecInfo);
01696     return NS_OK;
01697 }
01698 
01699 NS_IMETHODIMP
01700 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
01701 {
01702     nsAutoLock lock(mLock);
01703     NS_IF_ADDREF(*callbacks = mCallbacks);
01704     return NS_OK;
01705 }
01706 
01707 NS_IMETHODIMP
01708 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
01709 {
01710     nsAutoLock lock(mLock);
01711     mCallbacks = callbacks;
01712     // XXX should we tell PSM about this?
01713     return NS_OK;
01714 }
01715 
01716 NS_IMETHODIMP
01717 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
01718                                 nsIEventTarget *target)
01719 {
01720     nsCOMPtr<nsITransportEventSink> temp;
01721     if (target) {
01722         nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
01723                                                      sink, target);
01724         if (NS_FAILED(rv))
01725             return rv;
01726         sink = temp.get();
01727     }
01728 
01729     nsAutoLock lock(mLock);
01730     mEventSink = sink;
01731     return NS_OK;
01732 }
01733 
01734 NS_IMETHODIMP
01735 nsSocketTransport::IsAlive(PRBool *result)
01736 {
01737     *result = PR_FALSE;
01738 
01739     PRFileDesc *fd;
01740     {
01741         nsAutoLock lock(mLock);
01742         if (NS_FAILED(mCondition))
01743             return NS_OK;
01744         fd = GetFD_Locked();
01745         if (!fd)
01746             return NS_OK;
01747     }
01748 
01749     // XXX do some idle-time based checks??
01750 
01751     char c;
01752     PRInt32 rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
01753 
01754     if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
01755         *result = PR_TRUE;
01756 
01757     {
01758         nsAutoLock lock(mLock);
01759         ReleaseFD_Locked(fd);
01760     }
01761     return NS_OK;
01762 }
01763 
01764 NS_IMETHODIMP
01765 nsSocketTransport::GetHost(nsACString &host)
01766 {
01767     host = SocketHost();
01768     return NS_OK;
01769 }
01770 
01771 NS_IMETHODIMP
01772 nsSocketTransport::GetPort(PRInt32 *port)
01773 {
01774     *port = (PRInt32) SocketPort();
01775     return NS_OK;
01776 }
01777 
01778 NS_IMETHODIMP
01779 nsSocketTransport::GetPeerAddr(PRNetAddr *addr)
01780 {
01781     // once we are in the connected state, mNetAddr will not change.
01782     // so if we can verify that we are in the connected state, then
01783     // we can freely access mNetAddr from any thread without being
01784     // inside a critical section.
01785 
01786     NS_ENSURE_TRUE(mState == STATE_TRANSFERRING, NS_ERROR_NOT_AVAILABLE);
01787 
01788     memcpy(addr, &mNetAddr, sizeof(mNetAddr));
01789     return NS_OK;
01790 }
01791 
01792 NS_IMETHODIMP
01793 nsSocketTransport::GetSelfAddr(PRNetAddr *addr)
01794 {
01795     // we must not call any PR methods on our file descriptor
01796     // while holding mLock since those methods might re-enter
01797     // socket transport code.
01798 
01799     PRFileDesc *fd;
01800     {
01801         nsAutoLock lock(mLock);
01802         fd = GetFD_Locked();
01803     }
01804 
01805     if (!fd)
01806         return NS_ERROR_NOT_CONNECTED;
01807 
01808     nsresult rv =
01809         (PR_GetSockName(fd, addr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
01810 
01811     {
01812         nsAutoLock lock(mLock);
01813         ReleaseFD_Locked(fd);
01814     }
01815 
01816     return rv;
01817 }
01818 
01819 NS_IMETHODIMP
01820 nsSocketTransport::GetTimeout(PRUint32 type, PRUint32 *value)
01821 {
01822     NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
01823     *value = (PRUint32) mTimeouts[type];
01824     return NS_OK;
01825 }
01826 
01827 NS_IMETHODIMP
01828 nsSocketTransport::SetTimeout(PRUint32 type, PRUint32 value)
01829 {
01830     NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
01831     // truncate overly large timeout values.
01832     mTimeouts[type] = (PRUint16) PR_MIN(value, PR_UINT16_MAX);
01833     PostEvent(MSG_TIMEOUT_CHANGED);
01834     return NS_OK;
01835 }
01836 
01837 NS_IMETHODIMP
01838 nsSocketTransport::OnLookupComplete(nsICancelable *request,
01839                                     nsIDNSRecord  *rec,
01840                                     nsresult       status)
01841 {
01842     // flag host lookup complete for the benefit of the ResolveHost method.
01843     mResolving = PR_FALSE;
01844 
01845     nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
01846 
01847     // if posting a message fails, then we should assume that the socket
01848     // transport has been shutdown.  this should never happen!  if it does
01849     // it means that the socket transport service was shutdown before the
01850     // DNS service.
01851     if (NS_FAILED(rv))
01852         NS_WARNING("unable to post DNS lookup complete message");
01853 
01854     return NS_OK;
01855 }
01856 
01857 #ifdef ENABLE_SOCKET_TRACING
01858 
01859 #include <stdio.h>
01860 #include <ctype.h>
01861 #include "prenv.h"
01862 
01863 static void
01864 DumpBytesToFile(const char *path, const char *header, const char *buf, PRInt32 n)
01865 {
01866     FILE *fp = fopen(path, "a");
01867 
01868     fprintf(fp, "\n%s [%d bytes]\n", header, n);
01869 
01870     const unsigned char *p;
01871     while (n) {
01872         p = (const unsigned char *) buf;
01873 
01874         PRInt32 i, row_max = PR_MIN(16, n);
01875 
01876         for (i = 0; i < row_max; ++i)
01877             fprintf(fp, "%02x  ", *p++);
01878         for (i = row_max; i < 16; ++i)
01879             fprintf(fp, "    ");
01880 
01881         p = (const unsigned char *) buf;
01882         for (i = 0; i < row_max; ++i, ++p) {
01883             if (isprint(*p))
01884                 fprintf(fp, "%c", *p);
01885             else
01886                 fprintf(fp, ".");
01887         }
01888 
01889         fprintf(fp, "\n");
01890         buf += row_max;
01891         n -= row_max;
01892     }
01893 
01894     fprintf(fp, "\n");
01895     fclose(fp);
01896 }
01897 
01898 void
01899 nsSocketTransport::TraceInBuf(const char *buf, PRInt32 n)
01900 {
01901     char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
01902     if (!val || !*val)
01903         return;
01904 
01905     nsCAutoString header;
01906     header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
01907     header.Append(':');
01908     header.AppendInt(mPort);
01909 
01910     DumpBytesToFile(val, header.get(), buf, n);
01911 }
01912 
01913 void
01914 nsSocketTransport::TraceOutBuf(const char *buf, PRInt32 n)
01915 {
01916     char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
01917     if (!val || !*val)
01918         return;
01919 
01920     nsCAutoString header;
01921     header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
01922     header.Append(':');
01923     header.AppendInt(mPort);
01924 
01925     DumpBytesToFile(val, header.get(), buf, n);
01926 }
01927 
01928 #endif