1 /* vim:set ts=4 sw=4 et cindent: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2002
20 * the Initial Developer. All Rights Reserved.
23 * Darin Fisher <darin@netscape.com>
24 * Malcolm Smith <malsmith@cs.rmit.edu.au>
25 * Andreas Otte <andreas.otte@debitel.net>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
45 #include "nsSocketTransport2.h"
46 #include "nsIOService.h"
47 #include "nsStreamUtils.h"
48 #include "nsNetSegmentUtils.h"
49 #include "nsTransportUtils.h"
50 #include "nsProxyInfo.h"
52 #include "nsAutoLock.h"
53 #include "nsAutoPtr.h"
64 #include "nsIServiceManager.h"
65 #include "nsIProxyObjectManager.h"
66 #include "nsISocketProviderService.h"
67 #include "nsISocketProvider.h"
68 #include "nsISSLSocketControl.h"
69 #include "nsINSSErrorsService.h"
71 #include "nsIProgrammingLanguage.h"
72 #include "nsIClassInfoImpl.h"
75 #include "nsNativeConnectionHelper.h"
78 //-----------------------------------------------------------------------------
80 static NS_DEFINE_CID(kSocketProviderServiceCID
, NS_SOCKETPROVIDERSERVICE_CID
);
81 static NS_DEFINE_CID(kDNSServiceCID
, NS_DNSSERVICE_CID
);
83 //-----------------------------------------------------------------------------
85 class nsSocketEvent
: public nsRunnable
88 nsSocketEvent(nsSocketTransport
*transport
, PRUint32 type
,
89 nsresult status
= NS_OK
, nsISupports
*param
= nsnull
)
90 : mTransport(transport
)
98 mTransport
->OnSocketEvent(mType
, mStatus
, mParam
);
103 nsRefPtr
<nsSocketTransport
> mTransport
;
107 nsCOMPtr
<nsISupports
> mParam
;
110 //-----------------------------------------------------------------------------
112 //#define TEST_CONNECT_ERRORS
113 #ifdef TEST_CONNECT_ERRORS
115 static PRErrorCode
RandomizeConnectError(PRErrorCode code
)
118 // To test out these errors, load http://www.yahoo.com/. It should load
119 // correctly despite the random occurrence of these errors.
122 if (n
> RAND_MAX
/2) {
124 PRErrorCode err_code
;
125 const char *err_name
;
129 // These errors should be recoverable provided there is another
130 // IP address in mDNSRecord.
132 { PR_CONNECT_REFUSED_ERROR
, "PR_CONNECT_REFUSED_ERROR" },
133 { PR_CONNECT_TIMEOUT_ERROR
, "PR_CONNECT_TIMEOUT_ERROR" },
135 // This error will cause this socket transport to error out;
136 // however, if the consumer is HTTP, then the HTTP transaction
137 // should be restarted when this error occurs.
139 { PR_CONNECT_RESET_ERROR
, "PR_CONNECT_RESET_ERROR" },
141 n
= n
% (sizeof(errors
)/sizeof(errors
[0]));
142 code
= errors
[n
].err_code
;
143 LOG(("simulating NSPR error %d [%s]\n", code
, errors
[n
].err_name
));
149 //-----------------------------------------------------------------------------
152 IsNSSErrorCode(PRErrorCode code
)
155 ((code
>= nsINSSErrorsService::NSS_SEC_ERROR_BASE
) &&
156 (code
< nsINSSErrorsService::NSS_SEC_ERROR_LIMIT
))
158 ((code
>= nsINSSErrorsService::NSS_SSL_ERROR_BASE
) &&
159 (code
< nsINSSErrorsService::NSS_SSL_ERROR_LIMIT
));
162 // this logic is duplicated from the implementation of
163 // nsINSSErrorsService::getXPCOMFromNSSError
164 // It might have been better to implement that interface here...
166 GetXPCOMFromNSSError(PRErrorCode code
)
168 return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY
, -1 * code
);
172 ErrorAccordingToNSPR(PRErrorCode errorCode
)
174 nsresult rv
= NS_ERROR_FAILURE
;
176 case PR_WOULD_BLOCK_ERROR
:
177 rv
= NS_BASE_STREAM_WOULD_BLOCK
;
179 case PR_CONNECT_ABORTED_ERROR
:
180 case PR_CONNECT_RESET_ERROR
:
181 rv
= NS_ERROR_NET_RESET
;
183 case PR_END_OF_FILE_ERROR
: // XXX document this correlation
184 rv
= NS_ERROR_NET_INTERRUPT
;
186 case PR_CONNECT_REFUSED_ERROR
:
187 case PR_NETWORK_UNREACHABLE_ERROR
: // XXX need new nsresult for this!
188 case PR_HOST_UNREACHABLE_ERROR
: // XXX and this!
189 case PR_ADDRESS_NOT_AVAILABLE_ERROR
:
190 // Treat EACCES as a soft error since (at least on Linux) connect() returns
191 // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
192 case PR_ADDRESS_NOT_SUPPORTED_ERROR
:
193 case PR_NO_ACCESS_RIGHTS_ERROR
:
194 rv
= NS_ERROR_CONNECTION_REFUSED
;
196 case PR_IO_TIMEOUT_ERROR
:
197 case PR_CONNECT_TIMEOUT_ERROR
:
198 rv
= NS_ERROR_NET_TIMEOUT
;
201 if (IsNSSErrorCode(errorCode
))
202 rv
= GetXPCOMFromNSSError(errorCode
);
205 LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode
, rv
));
209 //-----------------------------------------------------------------------------
210 // socket input stream impl
211 //-----------------------------------------------------------------------------
213 nsSocketInputStream::nsSocketInputStream(nsSocketTransport
*trans
)
222 nsSocketInputStream::~nsSocketInputStream()
226 // called on the socket transport thread...
228 // condition : failure code if socket has been closed
231 nsSocketInputStream::OnSocketReady(nsresult condition
)
233 LOG(("nsSocketInputStream::OnSocketReady [this=%x cond=%x]\n",
236 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
238 nsCOMPtr
<nsIInputStreamCallback
> callback
;
240 nsAutoLock
lock(mTransport
->mLock
);
242 // update condition, but be careful not to erase an already
243 // existing error condition.
244 if (NS_SUCCEEDED(mCondition
))
245 mCondition
= condition
;
247 // ignore event if only waiting for closure and not closed.
248 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
249 callback
= mCallback
;
256 callback
->OnInputStreamReady(this);
259 NS_IMPL_QUERY_INTERFACE2(nsSocketInputStream
,
263 NS_IMETHODIMP_(nsrefcnt
)
264 nsSocketInputStream::AddRef()
266 PR_AtomicIncrement((PRInt32
*)&mReaderRefCnt
);
267 return mTransport
->AddRef();
270 NS_IMETHODIMP_(nsrefcnt
)
271 nsSocketInputStream::Release()
273 if (PR_AtomicDecrement((PRInt32
*)&mReaderRefCnt
) == 0)
275 return mTransport
->Release();
279 nsSocketInputStream::Close()
281 return CloseWithStatus(NS_BASE_STREAM_CLOSED
);
285 nsSocketInputStream::Available(PRUint32
*avail
)
287 LOG(("nsSocketInputStream::Available [this=%x]\n", this));
293 nsAutoLock
lock(mTransport
->mLock
);
295 if (NS_FAILED(mCondition
))
298 fd
= mTransport
->GetFD_Locked();
303 // cannot hold lock while calling NSPR. (worried about the fact that PSM
304 // synchronously proxies notifications over to the UI thread, which could
305 // mistakenly try to re-enter this code.)
306 PRInt32 n
= PR_Available(fd
);
310 nsAutoLock
lock(mTransport
->mLock
);
312 mTransport
->ReleaseFD_Locked(fd
);
317 PRErrorCode code
= PR_GetError();
318 if (code
== PR_WOULD_BLOCK_ERROR
)
320 mCondition
= ErrorAccordingToNSPR(code
);
325 mTransport
->OnInputClosed(rv
);
330 nsSocketInputStream::Read(char *buf
, PRUint32 count
, PRUint32
*countRead
)
332 LOG(("nsSocketInputStream::Read [this=%x count=%u]\n", this, count
));
338 nsAutoLock
lock(mTransport
->mLock
);
340 if (NS_FAILED(mCondition
))
341 return (mCondition
== NS_BASE_STREAM_CLOSED
) ? NS_OK
: mCondition
;
343 fd
= mTransport
->GetFD_Locked();
345 return NS_BASE_STREAM_WOULD_BLOCK
;
348 LOG((" calling PR_Read [count=%u]\n", count
));
350 // cannot hold lock while calling NSPR. (worried about the fact that PSM
351 // synchronously proxies notifications over to the UI thread, which could
352 // mistakenly try to re-enter this code.)
353 PRInt32 n
= PR_Read(fd
, buf
, count
);
355 LOG((" PR_Read returned [n=%d]\n", n
));
359 nsAutoLock
lock(mTransport
->mLock
);
361 #ifdef ENABLE_SOCKET_TRACING
363 mTransport
->TraceInBuf(buf
, n
);
366 mTransport
->ReleaseFD_Locked(fd
);
369 mByteCount
+= (*countRead
= n
);
371 PRErrorCode code
= PR_GetError();
372 if (code
== PR_WOULD_BLOCK_ERROR
)
373 return NS_BASE_STREAM_WOULD_BLOCK
;
374 mCondition
= ErrorAccordingToNSPR(code
);
379 mTransport
->OnInputClosed(rv
);
381 // only send this notification if we have indeed read some data.
382 // see bug 196827 for an example of why this is important.
384 mTransport
->SendStatus(nsISocketTransport::STATUS_RECEIVING_FROM
);
389 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer
, void *closure
,
390 PRUint32 count
, PRUint32
*countRead
)
392 // socket stream is unbuffered
393 return NS_ERROR_NOT_IMPLEMENTED
;
397 nsSocketInputStream::IsNonBlocking(PRBool
*nonblocking
)
399 *nonblocking
= PR_TRUE
;
404 nsSocketInputStream::CloseWithStatus(nsresult reason
)
406 LOG(("nsSocketInputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason
));
408 // may be called from any thread
412 nsAutoLock
lock(mTransport
->mLock
);
414 if (NS_SUCCEEDED(mCondition
))
415 rv
= mCondition
= reason
;
420 mTransport
->OnInputClosed(rv
);
425 nsSocketInputStream::AsyncWait(nsIInputStreamCallback
*callback
,
428 nsIEventTarget
*target
)
430 LOG(("nsSocketInputStream::AsyncWait [this=%x]\n", this));
432 // This variable will be non-null when we want to call the callback
433 // directly from this function, but outside the lock.
434 // (different from callback when target is not null)
435 nsCOMPtr
<nsIInputStreamCallback
> directCallback
;
437 nsAutoLock
lock(mTransport
->mLock
);
439 if (callback
&& target
) {
443 // failure to create an event proxy (most likely out of memory)
444 // shouldn't alter the state of the transport.
446 nsCOMPtr
<nsIInputStreamCallback
> temp
;
447 nsresult rv
= NS_NewInputStreamReadyEvent(getter_AddRefs(temp
),
449 if (NS_FAILED(rv
)) return rv
;
453 mCallback
= callback
;
455 if (NS_FAILED(mCondition
))
456 directCallback
.swap(mCallback
);
458 mCallbackFlags
= flags
;
461 directCallback
->OnInputStreamReady(this);
463 mTransport
->OnInputPending();
468 //-----------------------------------------------------------------------------
469 // socket output stream impl
470 //-----------------------------------------------------------------------------
472 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport
*trans
)
481 nsSocketOutputStream::~nsSocketOutputStream()
485 // called on the socket transport thread...
487 // condition : failure code if socket has been closed
490 nsSocketOutputStream::OnSocketReady(nsresult condition
)
492 LOG(("nsSocketOutputStream::OnSocketReady [this=%x cond=%x]\n",
495 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
497 nsCOMPtr
<nsIOutputStreamCallback
> callback
;
499 nsAutoLock
lock(mTransport
->mLock
);
501 // update condition, but be careful not to erase an already
502 // existing error condition.
503 if (NS_SUCCEEDED(mCondition
))
504 mCondition
= condition
;
506 // ignore event if only waiting for closure and not closed.
507 if (NS_FAILED(mCondition
) || !(mCallbackFlags
& WAIT_CLOSURE_ONLY
)) {
508 callback
= mCallback
;
515 callback
->OnOutputStreamReady(this);
518 NS_IMPL_QUERY_INTERFACE2(nsSocketOutputStream
,
520 nsIAsyncOutputStream
)
522 NS_IMETHODIMP_(nsrefcnt
)
523 nsSocketOutputStream::AddRef()
525 PR_AtomicIncrement((PRInt32
*)&mWriterRefCnt
);
526 return mTransport
->AddRef();
529 NS_IMETHODIMP_(nsrefcnt
)
530 nsSocketOutputStream::Release()
532 if (PR_AtomicDecrement((PRInt32
*)&mWriterRefCnt
) == 0)
534 return mTransport
->Release();
538 nsSocketOutputStream::Close()
540 return CloseWithStatus(NS_BASE_STREAM_CLOSED
);
544 nsSocketOutputStream::Flush()
550 nsSocketOutputStream::Write(const char *buf
, PRUint32 count
, PRUint32
*countWritten
)
552 LOG(("nsSocketOutputStream::Write [this=%x count=%u]\n", this, count
));
561 nsAutoLock
lock(mTransport
->mLock
);
563 if (NS_FAILED(mCondition
))
566 fd
= mTransport
->GetFD_Locked();
568 return NS_BASE_STREAM_WOULD_BLOCK
;
571 LOG((" calling PR_Write [count=%u]\n", count
));
573 // cannot hold lock while calling NSPR. (worried about the fact that PSM
574 // synchronously proxies notifications over to the UI thread, which could
575 // mistakenly try to re-enter this code.)
576 PRInt32 n
= PR_Write(fd
, buf
, count
);
578 LOG((" PR_Write returned [n=%d]\n", n
));
579 NS_ASSERTION(n
!= 0, "unexpected return value");
583 nsAutoLock
lock(mTransport
->mLock
);
585 #ifdef ENABLE_SOCKET_TRACING
587 mTransport
->TraceOutBuf(buf
, n
);
590 mTransport
->ReleaseFD_Locked(fd
);
593 mByteCount
+= (*countWritten
= n
);
595 PRErrorCode code
= PR_GetError();
596 if (code
== PR_WOULD_BLOCK_ERROR
)
597 return NS_BASE_STREAM_WOULD_BLOCK
;
598 mCondition
= ErrorAccordingToNSPR(code
);
603 mTransport
->OnOutputClosed(rv
);
605 // only send this notification if we have indeed written some data.
606 // see bug 196827 for an example of why this is important.
608 mTransport
->SendStatus(nsISocketTransport::STATUS_SENDING_TO
);
613 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader
, void *closure
,
614 PRUint32 count
, PRUint32
*countRead
)
616 // socket stream is unbuffered
617 return NS_ERROR_NOT_IMPLEMENTED
;
621 nsSocketOutputStream::WriteFromSegments(nsIInputStream
*input
,
623 const char *fromSegment
,
628 nsSocketOutputStream
*self
= (nsSocketOutputStream
*) closure
;
629 return self
->Write(fromSegment
, count
, countRead
);
633 nsSocketOutputStream::WriteFrom(nsIInputStream
*stream
, PRUint32 count
, PRUint32
*countRead
)
635 return stream
->ReadSegments(WriteFromSegments
, this, count
, countRead
);
639 nsSocketOutputStream::IsNonBlocking(PRBool
*nonblocking
)
641 *nonblocking
= PR_TRUE
;
646 nsSocketOutputStream::CloseWithStatus(nsresult reason
)
648 LOG(("nsSocketOutputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason
));
650 // may be called from any thread
654 nsAutoLock
lock(mTransport
->mLock
);
656 if (NS_SUCCEEDED(mCondition
))
657 rv
= mCondition
= reason
;
662 mTransport
->OnOutputClosed(rv
);
667 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback
*callback
,
670 nsIEventTarget
*target
)
672 LOG(("nsSocketOutputStream::AsyncWait [this=%x]\n", this));
675 nsAutoLock
lock(mTransport
->mLock
);
677 if (callback
&& target
) {
681 // failure to create an event proxy (most likely out of memory)
682 // shouldn't alter the state of the transport.
684 nsCOMPtr
<nsIOutputStreamCallback
> temp
;
685 nsresult rv
= NS_NewOutputStreamReadyEvent(getter_AddRefs(temp
),
687 if (NS_FAILED(rv
)) return rv
;
691 mCallback
= callback
;
693 mCallbackFlags
= flags
;
695 mTransport
->OnOutputPending();
699 //-----------------------------------------------------------------------------
700 // socket transport impl
701 //-----------------------------------------------------------------------------
703 nsSocketTransport::nsSocketTransport()
708 , mProxyTransparent(PR_FALSE
)
709 , mProxyTransparentResolvesHost(PR_FALSE
)
710 , mState(STATE_CLOSED
)
711 , mAttached(PR_FALSE
)
712 , mInputClosed(PR_TRUE
)
713 , mOutputClosed(PR_TRUE
)
714 , mResolving(PR_FALSE
)
715 , mLock(PR_NewLock())
718 , mFDconnected(PR_FALSE
)
722 LOG(("creating nsSocketTransport @%x\n", this));
724 NS_ADDREF(gSocketTransportService
);
726 mTimeouts
[TIMEOUT_CONNECT
] = PR_UINT16_MAX
; // no timeout
727 mTimeouts
[TIMEOUT_READ_WRITE
] = PR_UINT16_MAX
; // no timeout
730 nsSocketTransport::~nsSocketTransport()
732 LOG(("destroying nsSocketTransport @%x\n", this));
734 // cleanup socket type info
737 for (i
=0; i
<mTypeCount
; ++i
)
738 PL_strfree(mTypes
[i
]);
743 PR_DestroyLock(mLock
);
745 nsSocketTransportService
*serv
= gSocketTransportService
;
746 NS_RELEASE(serv
); // nulls argument
750 nsSocketTransport::Init(const char **types
, PRUint32 typeCount
,
751 const nsACString
&host
, PRUint16 port
,
752 nsIProxyInfo
*givenProxyInfo
)
755 return NS_ERROR_OUT_OF_MEMORY
;
757 nsCOMPtr
<nsProxyInfo
> proxyInfo
;
758 if (givenProxyInfo
) {
759 proxyInfo
= do_QueryInterface(givenProxyInfo
);
760 NS_ENSURE_ARG(proxyInfo
);
763 // init socket type info
768 const char *proxyType
= nsnull
;
770 mProxyPort
= proxyInfo
->Port();
771 mProxyHost
= proxyInfo
->Host();
772 // grab proxy type (looking for "socks" for example)
773 proxyType
= proxyInfo
->Type();
774 if (proxyType
&& (strcmp(proxyType
, "http") == 0 ||
775 strcmp(proxyType
, "direct") == 0 ||
776 strcmp(proxyType
, "unknown") == 0))
780 LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s:%hu]\n",
781 this, mHost
.get(), mPort
, mProxyHost
.get(), mProxyPort
));
783 // include proxy type as a socket type if proxy type is not "http"
784 mTypeCount
= typeCount
+ (proxyType
!= nsnull
);
788 // if we have socket types, then the socket provider service had
791 nsCOMPtr
<nsISocketProviderService
> spserv
=
792 do_GetService(kSocketProviderServiceCID
, &rv
);
793 if (NS_FAILED(rv
)) return rv
;
795 mTypes
= (char **) malloc(mTypeCount
* sizeof(char *));
797 return NS_ERROR_OUT_OF_MEMORY
;
799 // now verify that each socket type has a registered socket provider.
800 for (PRUint32 i
= 0, type
= 0; i
< mTypeCount
; ++i
) {
801 // store socket types
802 if (i
== 0 && proxyType
)
803 mTypes
[i
] = PL_strdup(proxyType
);
805 mTypes
[i
] = PL_strdup(types
[type
++]);
809 return NS_ERROR_OUT_OF_MEMORY
;
811 nsCOMPtr
<nsISocketProvider
> provider
;
812 rv
= spserv
->GetSocketProvider(mTypes
[i
], getter_AddRefs(provider
));
814 NS_WARNING("no registered socket provider");
818 // note if socket type corresponds to a transparent proxy
819 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
820 if ((strcmp(mTypes
[i
], "socks") == 0) ||
821 (strcmp(mTypes
[i
], "socks4") == 0)) {
822 mProxyTransparent
= PR_TRUE
;
824 if (proxyInfo
->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
) {
825 // we want the SOCKS layer to send the hostname
826 // and port to the proxy and let it do the DNS.
827 mProxyTransparentResolvesHost
= PR_TRUE
;
836 nsSocketTransport::InitWithConnectedSocket(PRFileDesc
*fd
, const PRNetAddr
*addr
)
839 return NS_ERROR_OUT_OF_MEMORY
;
841 NS_ASSERTION(!mFD
, "already initialized");
844 PR_NetAddrToString(addr
, buf
, sizeof(buf
));
848 if (addr
->raw
.family
== PR_AF_INET
)
849 port
= addr
->inet
.port
;
851 port
= addr
->ipv6
.port
;
852 mPort
= PR_ntohs(port
);
854 memcpy(&mNetAddr
, addr
, sizeof(PRNetAddr
));
856 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
857 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
858 mState
= STATE_TRANSFERRING
;
864 // make sure new socket is non-blocking
865 PRSocketOptionData opt
;
866 opt
.option
= PR_SockOpt_Nonblocking
;
867 opt
.value
.non_blocking
= PR_TRUE
;
868 PR_SetSocketOption(mFD
, &opt
);
870 LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
871 this, mHost
.get(), mPort
));
873 // jump to InitiateSocket to get ourselves attached to the STS poll list.
874 return PostEvent(MSG_RETRY_INIT_SOCKET
);
878 nsSocketTransport::PostEvent(PRUint32 type
, nsresult status
, nsISupports
*param
)
880 LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
881 this, type
, status
, param
));
883 nsCOMPtr
<nsIRunnable
> event
= new nsSocketEvent(this, type
, status
, param
);
885 return NS_ERROR_OUT_OF_MEMORY
;
887 return gSocketTransportService
->Dispatch(event
, NS_DISPATCH_NORMAL
);
891 nsSocketTransport::SendStatus(nsresult status
)
893 LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status
));
895 nsCOMPtr
<nsITransportEventSink
> sink
;
898 nsAutoLock
lock(mLock
);
901 case STATUS_SENDING_TO
:
902 progress
= mOutput
.ByteCount();
904 case STATUS_RECEIVING_FROM
:
905 progress
= mInput
.ByteCount();
913 sink
->OnTransportStatus(this, status
, progress
, LL_MAXUINT
);
917 nsSocketTransport::ResolveHost()
919 LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
923 if (!mProxyHost
.IsEmpty()) {
924 if (!mProxyTransparent
|| mProxyTransparentResolvesHost
) {
925 // When not resolving mHost locally, we still want to ensure that
926 // it only contains valid characters. See bug 304904 for details.
927 if (!net_IsValidHostName(mHost
))
928 return NS_ERROR_UNKNOWN_HOST
;
930 if (mProxyTransparentResolvesHost
) {
931 // Name resolution is done on the server side. Just pretend
932 // client resolution is complete, this will get picked up later.
933 // since we don't need to do DNS now, we bypass the resolving
934 // step by initializing mNetAddr to an empty address, but we
935 // must keep the port. The SOCKS IO layer will use the hostname
936 // we send it when it's created, rather than the empty address
937 // we send with the connect call.
938 mState
= STATE_RESOLVING
;
939 PR_SetNetAddr(PR_IpAddrAny
, PR_AF_INET
, SocketPort(), &mNetAddr
);
940 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nsnull
);
944 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(kDNSServiceCID
, &rv
);
945 if (NS_FAILED(rv
)) return rv
;
947 mResolving
= PR_TRUE
;
949 rv
= dns
->AsyncResolve(SocketHost(), 0, this, nsnull
,
950 getter_AddRefs(mDNSRequest
));
951 if (NS_SUCCEEDED(rv
)) {
952 LOG((" advancing to STATE_RESOLVING\n"));
953 mState
= STATE_RESOLVING
;
954 // only report that we are resolving if we are still resolving...
956 SendStatus(STATUS_RESOLVING
);
962 nsSocketTransport::BuildSocket(PRFileDesc
*&fd
, PRBool
&proxyTransparent
, PRBool
&usingSSL
)
964 LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
968 proxyTransparent
= PR_FALSE
;
971 if (mTypeCount
== 0) {
972 fd
= PR_OpenTCPSocket(mNetAddr
.raw
.family
);
973 rv
= fd
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
978 nsCOMPtr
<nsISocketProviderService
> spserv
=
979 do_GetService(kSocketProviderServiceCID
, &rv
);
980 if (NS_FAILED(rv
)) return rv
;
982 const char *host
= mHost
.get();
983 PRInt32 port
= (PRInt32
) mPort
;
984 const char *proxyHost
= mProxyHost
.IsEmpty() ? nsnull
: mProxyHost
.get();
985 PRInt32 proxyPort
= (PRInt32
) mProxyPort
;
986 PRUint32 proxyFlags
= 0;
989 for (i
=0; i
<mTypeCount
; ++i
) {
990 nsCOMPtr
<nsISocketProvider
> provider
;
992 LOG((" pushing io layer [%u:%s]\n", i
, mTypes
[i
]));
994 rv
= spserv
->GetSocketProvider(mTypes
[i
], getter_AddRefs(provider
));
998 if (mProxyTransparentResolvesHost
)
999 proxyFlags
|= nsISocketProvider::PROXY_RESOLVES_HOST
;
1001 nsCOMPtr
<nsISupports
> secinfo
;
1003 // if this is the first type, we'll want the
1004 // service to allocate a new socket
1005 rv
= provider
->NewSocket(mNetAddr
.raw
.family
,
1006 host
, port
, proxyHost
, proxyPort
,
1008 getter_AddRefs(secinfo
));
1010 if (NS_SUCCEEDED(rv
) && !fd
) {
1011 NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
1012 rv
= NS_ERROR_UNEXPECTED
;
1016 // the socket has already been allocated,
1017 // so we just want the service to add itself
1018 // to the stack (such as pushing an io layer)
1019 rv
= provider
->AddToSocket(mNetAddr
.raw
.family
,
1020 host
, port
, proxyHost
, proxyPort
,
1022 getter_AddRefs(secinfo
));
1028 // if the service was ssl or starttls, we want to hold onto the socket info
1029 PRBool isSSL
= (strcmp(mTypes
[i
], "ssl") == 0);
1030 if (isSSL
|| (strcmp(mTypes
[i
], "starttls") == 0)) {
1031 // remember security info and give notification callbacks to PSM...
1032 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1034 nsAutoLock
lock(mLock
);
1036 callbacks
= mCallbacks
;
1037 LOG((" [secinfo=%x callbacks=%x]\n", mSecInfo
.get(), mCallbacks
.get()));
1039 // don't call into PSM while holding mLock!!
1040 nsCOMPtr
<nsISSLSocketControl
> secCtrl(do_QueryInterface(secinfo
));
1042 secCtrl
->SetNotificationCallbacks(callbacks
);
1043 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1046 else if ((strcmp(mTypes
[i
], "socks") == 0) ||
1047 (strcmp(mTypes
[i
], "socks4") == 0)) {
1048 // since socks is transparent, any layers above
1049 // it do not have to worry about proxy stuff
1052 proxyTransparent
= PR_TRUE
;
1056 if (NS_FAILED(rv
)) {
1057 LOG((" error pushing io layer [%u:%s rv=%x]\n", i
, mTypes
[i
], rv
));
1067 nsSocketTransport::InitiateSocket()
1069 LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
1074 // find out if it is going to be ok to attach another socket to the STS.
1075 // if not then we have to wait for the STS to tell us that it is ok.
1076 // the notification is asynchronous, which means that when we could be
1077 // in a race to call AttachSocket once notified. for this reason, when
1078 // we get notified, we just re-enter this function. as a result, we are
1079 // sure to ask again before calling AttachSocket. in this way we deal
1080 // with the race condition. though it isn't the most elegant solution,
1081 // it is far simpler than trying to build a system that would guarantee
1082 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1083 // 194402 for more info.
1085 if (!gSocketTransportService
->CanAttachSocket()) {
1086 nsCOMPtr
<nsIRunnable
> event
=
1087 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET
);
1089 return NS_ERROR_OUT_OF_MEMORY
;
1090 return gSocketTransportService
->NotifyWhenCanAttachSocket(event
);
1094 // if we already have a connected socket, then just attach and return.
1097 rv
= gSocketTransportService
->AttachSocket(mFD
, this);
1098 if (NS_SUCCEEDED(rv
))
1099 mAttached
= PR_TRUE
;
1104 // create new socket fd, push io layers, etc.
1107 PRBool proxyTransparent
;
1110 rv
= BuildSocket(fd
, proxyTransparent
, usingSSL
);
1111 if (NS_FAILED(rv
)) {
1112 LOG((" BuildSocket failed [rv=%x]\n", rv
));
1118 // Make the socket non-blocking...
1119 PRSocketOptionData opt
;
1120 opt
.option
= PR_SockOpt_Nonblocking
;
1121 opt
.value
.non_blocking
= PR_TRUE
;
1122 status
= PR_SetSocketOption(fd
, &opt
);
1123 NS_ASSERTION(status
== PR_SUCCESS
, "unable to make socket non-blocking");
1125 // inform socket transport about this newly created socket...
1126 rv
= gSocketTransportService
->AttachSocket(fd
, this);
1127 if (NS_FAILED(rv
)) {
1131 mAttached
= PR_TRUE
;
1133 // assign mFD so that we can properly handle OnSocketDetached before we've
1134 // established a connection.
1136 nsAutoLock
lock(mLock
);
1139 mFDconnected
= PR_FALSE
;
1142 LOG((" advancing to STATE_CONNECTING\n"));
1143 mState
= STATE_CONNECTING
;
1144 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1145 SendStatus(STATUS_CONNECTING_TO
);
1147 #if defined(PR_LOGGING)
1148 if (LOG_ENABLED()) {
1150 PR_NetAddrToString(&mNetAddr
, buf
, sizeof(buf
));
1151 LOG((" trying address: %s\n", buf
));
1156 // Initiate the connect() to the host...
1158 status
= PR_Connect(fd
, &mNetAddr
, NS_SOCKET_CONNECT_TIMEOUT
);
1159 if (status
== PR_SUCCESS
) {
1161 // we are connected!
1163 OnSocketConnected();
1166 PRErrorCode code
= PR_GetError();
1167 #if defined(TEST_CONNECT_ERRORS)
1168 code
= RandomizeConnectError(code
);
1171 // If the PR_Connect(...) would block, then poll for a connection.
1173 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
))
1174 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1176 // If the socket is already connected, then return success...
1178 else if (PR_IS_CONNECTED_ERROR
== code
) {
1180 // we are connected!
1182 OnSocketConnected();
1184 if (mSecInfo
&& !mProxyHost
.IsEmpty() && proxyTransparent
&& usingSSL
) {
1185 // if the connection phase is finished, and the ssl layer has
1186 // been pushed, and we were proxying (transparently; ie. nothing
1187 // has to happen in the protocol layer above us), it's time for
1188 // the ssl to start doing it's thing.
1189 nsCOMPtr
<nsISSLSocketControl
> secCtrl
=
1190 do_QueryInterface(mSecInfo
);
1192 LOG((" calling ProxyStartSSL()\n"));
1193 secCtrl
->ProxyStartSSL();
1195 // XXX what if we were forced to poll on the socket for a successful
1196 // connection... wouldn't we need to call ProxyStartSSL after a call
1197 // to PR_ConnectContinue indicates that we are connected?
1199 // XXX this appears to be what the old socket transport did. why
1200 // isn't this broken?
1204 // The connection was refused...
1207 rv
= ErrorAccordingToNSPR(code
);
1208 if ((rv
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty())
1209 rv
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1216 nsSocketTransport::RecoverFromError()
1218 NS_ASSERTION(NS_FAILED(mCondition
), "there should be something wrong");
1220 LOG(("nsSocketTransport::RecoverFromError [this=%x state=%x cond=%x]\n",
1221 this, mState
, mCondition
));
1223 // can only recover from errors in these states
1224 if (mState
!= STATE_RESOLVING
&& mState
!= STATE_CONNECTING
)
1227 // OK to check this outside mLock
1228 NS_ASSERTION(!mFDconnected
, "socket should not be connected");
1230 // can only recover from these errors
1231 if (mCondition
!= NS_ERROR_CONNECTION_REFUSED
&&
1232 mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
1233 mCondition
!= NS_ERROR_NET_TIMEOUT
&&
1234 mCondition
!= NS_ERROR_UNKNOWN_HOST
&&
1235 mCondition
!= NS_ERROR_UNKNOWN_PROXY_HOST
)
1238 PRBool tryAgain
= PR_FALSE
;
1240 // try next ip address only if past the resolver stage...
1241 if (mState
== STATE_CONNECTING
&& mDNSRecord
) {
1242 nsresult rv
= mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1243 if (NS_SUCCEEDED(rv
)) {
1244 LOG((" trying again with next ip address\n"));
1249 #if defined(XP_WIN) && !defined(WINCE)
1250 // If not trying next address, try to make a connection using dialup.
1251 // Retry if that connection is made.
1253 PRBool autodialEnabled
;
1254 gSocketTransportService
->GetAutodialEnabled(&autodialEnabled
);
1255 if (autodialEnabled
) {
1256 tryAgain
= nsNativeConnectionHelper::OnConnectionFailed(
1257 NS_ConvertUTF8toUTF16(SocketHost()).get());
1262 // prepare to try again.
1267 if (mState
== STATE_CONNECTING
) {
1268 mState
= STATE_RESOLVING
;
1269 msg
= MSG_DNS_LOOKUP_COMPLETE
;
1272 mState
= STATE_CLOSED
;
1273 msg
= MSG_ENSURE_CONNECT
;
1276 rv
= PostEvent(msg
, NS_OK
);
1278 tryAgain
= PR_FALSE
;
1284 // called on the socket thread only
1286 nsSocketTransport::OnMsgInputClosed(nsresult reason
)
1288 LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
1291 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
1293 mInputClosed
= PR_TRUE
;
1294 // check if event should affect entire transport
1295 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
))
1296 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1297 else if (mOutputClosed
)
1298 mCondition
= NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1300 if (mState
== STATE_TRANSFERRING
)
1301 mPollFlags
&= ~PR_POLL_READ
;
1302 mInput
.OnSocketReady(reason
);
1306 // called on the socket thread only
1308 nsSocketTransport::OnMsgOutputClosed(nsresult reason
)
1310 LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
1313 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
1315 mOutputClosed
= PR_TRUE
;
1316 // check if event should affect entire transport
1317 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
))
1318 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1319 else if (mInputClosed
)
1320 mCondition
= NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1322 if (mState
== STATE_TRANSFERRING
)
1323 mPollFlags
&= ~PR_POLL_WRITE
;
1324 mOutput
.OnSocketReady(reason
);
1329 nsSocketTransport::OnSocketConnected()
1331 LOG((" advancing to STATE_TRANSFERRING\n"));
1333 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
1334 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1335 mState
= STATE_TRANSFERRING
;
1337 // assign mFD (must do this within the transport lock), but take care not
1338 // to trample over mFDref if mFD is already set.
1340 nsAutoLock
lock(mLock
);
1341 NS_ASSERTION(mFD
, "no socket");
1342 NS_ASSERTION(mFDref
== 1, "wrong socket ref count");
1343 mFDconnected
= PR_TRUE
;
1346 SendStatus(STATUS_CONNECTED_TO
);
1350 nsSocketTransport::GetFD_Locked()
1352 // mFD is not available to the streams while disconnected.
1363 nsSocketTransport::ReleaseFD_Locked(PRFileDesc
*fd
)
1365 NS_ASSERTION(mFD
== fd
, "wrong fd");
1367 if (--mFDref
== 0) {
1368 LOG(("nsSocketTransport: calling PR_Close [this=%x]\n", this));
1374 //-----------------------------------------------------------------------------
1375 // socket event handler impl
1378 nsSocketTransport::OnSocketEvent(PRUint32 type
, nsresult status
, nsISupports
*param
)
1380 LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
1381 this, type
, status
, param
));
1383 if (NS_FAILED(mCondition
)) {
1384 // block event since we're apparently already dead.
1385 LOG((" blocking event [condition=%x]\n", mCondition
));
1387 // notify input/output streams in case either has a pending notify.
1389 mInput
.OnSocketReady(mCondition
);
1390 mOutput
.OnSocketReady(mCondition
);
1395 case MSG_ENSURE_CONNECT
:
1396 LOG((" MSG_ENSURE_CONNECT\n"));
1398 // ensure that we have created a socket, attached it, and have a
1401 if (mState
== STATE_CLOSED
)
1402 mCondition
= ResolveHost();
1404 LOG((" ignoring redundant event\n"));
1407 case MSG_DNS_LOOKUP_COMPLETE
:
1408 LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1411 mDNSRecord
= static_cast<nsIDNSRecord
*>(param
);
1412 mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1414 // status contains DNS lookup status
1415 if (NS_FAILED(status
)) {
1416 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1417 // proxy host is not found, so we fixup the error code.
1418 // For SOCKS proxies (mProxyTransparent == true), the socket
1419 // transport resolves the real host here, so there's no fixup
1420 // (see bug 226943).
1421 if ((status
== NS_ERROR_UNKNOWN_HOST
) && !mProxyTransparent
&&
1422 !mProxyHost
.IsEmpty())
1423 mCondition
= NS_ERROR_UNKNOWN_PROXY_HOST
;
1425 mCondition
= status
;
1427 else if (mState
== STATE_RESOLVING
)
1428 mCondition
= InitiateSocket();
1431 case MSG_RETRY_INIT_SOCKET
:
1432 mCondition
= InitiateSocket();
1435 case MSG_INPUT_CLOSED
:
1436 LOG((" MSG_INPUT_CLOSED\n"));
1437 OnMsgInputClosed(status
);
1440 case MSG_INPUT_PENDING
:
1441 LOG((" MSG_INPUT_PENDING\n"));
1442 OnMsgInputPending();
1445 case MSG_OUTPUT_CLOSED
:
1446 LOG((" MSG_OUTPUT_CLOSED\n"));
1447 OnMsgOutputClosed(status
);
1450 case MSG_OUTPUT_PENDING
:
1451 LOG((" MSG_OUTPUT_PENDING\n"));
1452 OnMsgOutputPending();
1454 case MSG_TIMEOUT_CHANGED
:
1455 LOG((" MSG_TIMEOUT_CHANGED\n"));
1456 mPollTimeout
= mTimeouts
[(mState
== STATE_TRANSFERRING
)
1457 ? TIMEOUT_READ_WRITE
: TIMEOUT_CONNECT
];
1460 LOG((" unhandled event!\n"));
1463 if (NS_FAILED(mCondition
)) {
1464 LOG((" after event [this=%x cond=%x]\n", this, mCondition
));
1465 if (!mAttached
) // need to process this error ourselves...
1466 OnSocketDetached(nsnull
);
1468 else if (mPollFlags
== PR_POLL_EXCEPT
)
1469 mPollFlags
= 0; // make idle
1472 //-----------------------------------------------------------------------------
1473 // socket handler impl
1476 nsSocketTransport::OnSocketReady(PRFileDesc
*fd
, PRInt16 outFlags
)
1478 LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
1481 if (outFlags
== -1) {
1482 LOG(("socket timeout expired\n"));
1483 mCondition
= NS_ERROR_NET_TIMEOUT
;
1487 if (mState
== STATE_TRANSFERRING
) {
1488 // if waiting to write and socket is writable or hit an exception.
1489 if ((mPollFlags
& PR_POLL_WRITE
) && (outFlags
& ~PR_POLL_READ
)) {
1490 // assume that we won't need to poll any longer (the stream will
1491 // request that we poll again if it is still pending).
1492 mPollFlags
&= ~PR_POLL_WRITE
;
1493 mOutput
.OnSocketReady(NS_OK
);
1495 // if waiting to read and socket is readable or hit an exception.
1496 if ((mPollFlags
& PR_POLL_READ
) && (outFlags
& ~PR_POLL_WRITE
)) {
1497 // assume that we won't need to poll any longer (the stream will
1498 // request that we poll again if it is still pending).
1499 mPollFlags
&= ~PR_POLL_READ
;
1500 mInput
.OnSocketReady(NS_OK
);
1502 // Update poll timeout in case it was changed
1503 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1505 else if (mState
== STATE_CONNECTING
) {
1506 PRStatus status
= PR_ConnectContinue(fd
, outFlags
);
1507 if (status
== PR_SUCCESS
) {
1509 // we are connected!
1511 OnSocketConnected();
1514 PRErrorCode code
= PR_GetError();
1515 #if defined(TEST_CONNECT_ERRORS)
1516 code
= RandomizeConnectError(code
);
1519 // If the connect is still not ready, then continue polling...
1521 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
1522 // Set up the select flags for connect...
1523 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1524 // Update poll timeout in case it was changed
1525 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1529 // else, the connection failed...
1531 mCondition
= ErrorAccordingToNSPR(code
);
1532 if ((mCondition
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty())
1533 mCondition
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1534 LOG((" connection failed! [reason=%x]\n", mCondition
));
1539 NS_ERROR("unexpected socket state");
1540 mCondition
= NS_ERROR_UNEXPECTED
;
1543 if (mPollFlags
== PR_POLL_EXCEPT
)
1544 mPollFlags
= 0; // make idle
1547 // called on the socket thread only
1549 nsSocketTransport::OnSocketDetached(PRFileDesc
*fd
)
1551 LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
1554 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
1556 // if we didn't initiate this detach, then be sure to pass an error
1557 // condition up to our consumers. (e.g., STS is shutting down.)
1558 if (NS_SUCCEEDED(mCondition
))
1559 mCondition
= NS_ERROR_ABORT
;
1561 if (RecoverFromError())
1564 mState
= STATE_CLOSED
;
1566 // make sure there isn't any pending DNS request
1568 mDNSRequest
->Cancel(NS_ERROR_ABORT
);
1573 // notify input/output streams
1575 mInput
.OnSocketReady(mCondition
);
1576 mOutput
.OnSocketReady(mCondition
);
1579 // break any potential reference cycle between the security info object
1580 // and ourselves by resetting its notification callbacks object. see
1581 // bug 285991 for details.
1582 nsCOMPtr
<nsISSLSocketControl
> secCtrl
= do_QueryInterface(mSecInfo
);
1584 secCtrl
->SetNotificationCallbacks(nsnull
);
1586 // finally, release our reference to the socket (must do this within
1587 // the transport lock) possibly closing the socket. Also release our
1588 // listeners to break potential refcount cycles.
1590 nsAutoLock
lock(mLock
);
1592 ReleaseFD_Locked(mFD
);
1593 // flag mFD as unusable; this prevents other consumers from
1594 // acquiring a reference to mFD.
1595 mFDconnected
= PR_FALSE
;
1597 mCallbacks
= nsnull
;
1598 mEventSink
= nsnull
;
1602 //-----------------------------------------------------------------------------
1605 NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport
,
1610 NS_IMPL_CI_INTERFACE_GETTER3(nsSocketTransport
,
1616 nsSocketTransport::OpenInputStream(PRUint32 flags
,
1619 nsIInputStream
**result
)
1621 LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
1624 NS_ENSURE_TRUE(!mInput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
1627 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
1629 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
1630 // XXX if the caller wants blocking, then the caller also gets buffered!
1631 //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
1632 PRBool openBlocking
= (flags
& OPEN_BLOCKING
);
1634 net_ResolveSegmentParams(segsize
, segcount
);
1635 nsIMemory
*segalloc
= net_GetSegmentAlloc(segsize
);
1638 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
1639 rv
= NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
),
1640 !openBlocking
, PR_TRUE
, segsize
, segcount
, segalloc
);
1641 if (NS_FAILED(rv
)) return rv
;
1643 // async copy from socket to pipe
1644 rv
= NS_AsyncCopy(&mInput
, pipeOut
, gSocketTransportService
,
1645 NS_ASYNCCOPY_VIA_WRITESEGMENTS
, segsize
);
1646 if (NS_FAILED(rv
)) return rv
;
1653 // flag input stream as open
1654 mInputClosed
= PR_FALSE
;
1656 rv
= PostEvent(MSG_ENSURE_CONNECT
);
1657 if (NS_FAILED(rv
)) return rv
;
1664 nsSocketTransport::OpenOutputStream(PRUint32 flags
,
1667 nsIOutputStream
**result
)
1669 LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
1672 NS_ENSURE_TRUE(!mOutput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
1675 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
1676 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
1677 // XXX if the caller wants blocking, then the caller also gets buffered!
1678 //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
1679 PRBool openBlocking
= (flags
& OPEN_BLOCKING
);
1681 net_ResolveSegmentParams(segsize
, segcount
);
1682 nsIMemory
*segalloc
= net_GetSegmentAlloc(segsize
);
1685 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
1686 rv
= NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
),
1687 PR_TRUE
, !openBlocking
, segsize
, segcount
, segalloc
);
1688 if (NS_FAILED(rv
)) return rv
;
1690 // async copy from socket to pipe
1691 rv
= NS_AsyncCopy(pipeIn
, &mOutput
, gSocketTransportService
,
1692 NS_ASYNCCOPY_VIA_READSEGMENTS
, segsize
);
1693 if (NS_FAILED(rv
)) return rv
;
1700 // flag output stream as open
1701 mOutputClosed
= PR_FALSE
;
1703 rv
= PostEvent(MSG_ENSURE_CONNECT
);
1704 if (NS_FAILED(rv
)) return rv
;
1711 nsSocketTransport::Close(nsresult reason
)
1713 if (NS_SUCCEEDED(reason
))
1714 reason
= NS_BASE_STREAM_CLOSED
;
1716 mInput
.CloseWithStatus(reason
);
1717 mOutput
.CloseWithStatus(reason
);
1722 nsSocketTransport::GetSecurityInfo(nsISupports
**secinfo
)
1724 nsAutoLock
lock(mLock
);
1725 NS_IF_ADDREF(*secinfo
= mSecInfo
);
1730 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor
**callbacks
)
1732 nsAutoLock
lock(mLock
);
1733 NS_IF_ADDREF(*callbacks
= mCallbacks
);
1738 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor
*callbacks
)
1740 nsAutoLock
lock(mLock
);
1741 mCallbacks
= callbacks
;
1742 // XXX should we tell PSM about this?
1747 nsSocketTransport::SetEventSink(nsITransportEventSink
*sink
,
1748 nsIEventTarget
*target
)
1750 nsCOMPtr
<nsITransportEventSink
> temp
;
1752 nsresult rv
= net_NewTransportEventSinkProxy(getter_AddRefs(temp
),
1759 nsAutoLock
lock(mLock
);
1765 nsSocketTransport::IsAlive(PRBool
*result
)
1771 nsAutoLock
lock(mLock
);
1772 if (NS_FAILED(mCondition
))
1774 fd
= GetFD_Locked();
1779 // XXX do some idle-time based checks??
1782 PRInt32 rval
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
1784 if ((rval
> 0) || (rval
< 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR
))
1788 nsAutoLock
lock(mLock
);
1789 ReleaseFD_Locked(fd
);
1795 nsSocketTransport::GetHost(nsACString
&host
)
1797 host
= SocketHost();
1802 nsSocketTransport::GetPort(PRInt32
*port
)
1804 *port
= (PRInt32
) SocketPort();
1809 nsSocketTransport::GetPeerAddr(PRNetAddr
*addr
)
1811 // once we are in the connected state, mNetAddr will not change.
1812 // so if we can verify that we are in the connected state, then
1813 // we can freely access mNetAddr from any thread without being
1814 // inside a critical section.
1816 NS_ENSURE_TRUE(mState
== STATE_TRANSFERRING
, NS_ERROR_NOT_AVAILABLE
);
1818 memcpy(addr
, &mNetAddr
, sizeof(mNetAddr
));
1823 nsSocketTransport::GetSelfAddr(PRNetAddr
*addr
)
1825 // we must not call any PR methods on our file descriptor
1826 // while holding mLock since those methods might re-enter
1827 // socket transport code.
1831 nsAutoLock
lock(mLock
);
1832 fd
= GetFD_Locked();
1836 return NS_ERROR_NOT_CONNECTED
;
1839 (PR_GetSockName(fd
, addr
) == PR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
1842 nsAutoLock
lock(mLock
);
1843 ReleaseFD_Locked(fd
);
1850 nsSocketTransport::GetTimeout(PRUint32 type
, PRUint32
*value
)
1852 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
1853 *value
= (PRUint32
) mTimeouts
[type
];
1858 nsSocketTransport::SetTimeout(PRUint32 type
, PRUint32 value
)
1860 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
1861 // truncate overly large timeout values.
1862 mTimeouts
[type
] = (PRUint16
) PR_MIN(value
, PR_UINT16_MAX
);
1863 PostEvent(MSG_TIMEOUT_CHANGED
);
1868 nsSocketTransport::OnLookupComplete(nsICancelable
*request
,
1872 // flag host lookup complete for the benefit of the ResolveHost method.
1873 mResolving
= PR_FALSE
;
1875 nsresult rv
= PostEvent(MSG_DNS_LOOKUP_COMPLETE
, status
, rec
);
1877 // if posting a message fails, then we should assume that the socket
1878 // transport has been shutdown. this should never happen! if it does
1879 // it means that the socket transport service was shutdown before the
1882 NS_WARNING("unable to post DNS lookup complete message");
1888 nsSocketTransport::GetInterfaces(PRUint32
*count
, nsIID
* **array
)
1890 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport
)(count
, array
);
1894 nsSocketTransport::GetHelperForLanguage(PRUint32 language
, nsISupports
**_retval
)
1901 nsSocketTransport::GetContractID(char * *aContractID
)
1903 *aContractID
= nsnull
;
1908 nsSocketTransport::GetClassDescription(char * *aClassDescription
)
1910 *aClassDescription
= nsnull
;
1915 nsSocketTransport::GetClassID(nsCID
* *aClassID
)
1922 nsSocketTransport::GetImplementationLanguage(PRUint32
*aImplementationLanguage
)
1924 *aImplementationLanguage
= nsIProgrammingLanguage::CPLUSPLUS
;
1929 nsSocketTransport::GetFlags(PRUint32
*aFlags
)
1931 *aFlags
= nsIClassInfo::THREADSAFE
;
1936 nsSocketTransport::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
)
1938 return NS_ERROR_NOT_AVAILABLE
;
1942 #ifdef ENABLE_SOCKET_TRACING
1949 DumpBytesToFile(const char *path
, const char *header
, const char *buf
, PRInt32 n
)
1951 FILE *fp
= fopen(path
, "a");
1953 fprintf(fp
, "\n%s [%d bytes]\n", header
, n
);
1955 const unsigned char *p
;
1957 p
= (const unsigned char *) buf
;
1959 PRInt32 i
, row_max
= PR_MIN(16, n
);
1961 for (i
= 0; i
< row_max
; ++i
)
1962 fprintf(fp
, "%02x ", *p
++);
1963 for (i
= row_max
; i
< 16; ++i
)
1966 p
= (const unsigned char *) buf
;
1967 for (i
= 0; i
< row_max
; ++i
, ++p
) {
1969 fprintf(fp
, "%c", *p
);
1984 nsSocketTransport::TraceInBuf(const char *buf
, PRInt32 n
)
1986 char *val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
1990 nsCAutoString header
;
1991 header
.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost
);
1993 header
.AppendInt(mPort
);
1995 DumpBytesToFile(val
, header
.get(), buf
, n
);
1999 nsSocketTransport::TraceOutBuf(const char *buf
, PRInt32 n
)
2001 char *val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2005 nsCAutoString header
;
2006 header
.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost
);
2008 header
.AppendInt(mPort
);
2010 DumpBytesToFile(val
, header
.get(), buf
, n
);