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 , mConnectionFlags(0)
711 , mState(STATE_CLOSED
)
712 , mAttached(PR_FALSE
)
713 , mInputClosed(PR_TRUE
)
714 , mOutputClosed(PR_TRUE
)
715 , mResolving(PR_FALSE
)
716 , mLock(PR_NewLock())
719 , mFDconnected(PR_FALSE
)
723 LOG(("creating nsSocketTransport @%x\n", this));
725 NS_ADDREF(gSocketTransportService
);
727 mTimeouts
[TIMEOUT_CONNECT
] = PR_UINT16_MAX
; // no timeout
728 mTimeouts
[TIMEOUT_READ_WRITE
] = PR_UINT16_MAX
; // no timeout
731 nsSocketTransport::~nsSocketTransport()
733 LOG(("destroying nsSocketTransport @%x\n", this));
735 // cleanup socket type info
738 for (i
=0; i
<mTypeCount
; ++i
)
739 PL_strfree(mTypes
[i
]);
744 PR_DestroyLock(mLock
);
746 nsSocketTransportService
*serv
= gSocketTransportService
;
747 NS_RELEASE(serv
); // nulls argument
751 nsSocketTransport::Init(const char **types
, PRUint32 typeCount
,
752 const nsACString
&host
, PRUint16 port
,
753 nsIProxyInfo
*givenProxyInfo
)
756 return NS_ERROR_OUT_OF_MEMORY
;
758 nsCOMPtr
<nsProxyInfo
> proxyInfo
;
759 if (givenProxyInfo
) {
760 proxyInfo
= do_QueryInterface(givenProxyInfo
);
761 NS_ENSURE_ARG(proxyInfo
);
764 // init socket type info
769 const char *proxyType
= nsnull
;
771 mProxyPort
= proxyInfo
->Port();
772 mProxyHost
= proxyInfo
->Host();
773 // grab proxy type (looking for "socks" for example)
774 proxyType
= proxyInfo
->Type();
775 if (proxyType
&& (strcmp(proxyType
, "http") == 0 ||
776 strcmp(proxyType
, "direct") == 0 ||
777 strcmp(proxyType
, "unknown") == 0))
781 LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s:%hu]\n",
782 this, mHost
.get(), mPort
, mProxyHost
.get(), mProxyPort
));
784 // include proxy type as a socket type if proxy type is not "http"
785 mTypeCount
= typeCount
+ (proxyType
!= nsnull
);
789 // if we have socket types, then the socket provider service had
792 nsCOMPtr
<nsISocketProviderService
> spserv
=
793 do_GetService(kSocketProviderServiceCID
, &rv
);
794 if (NS_FAILED(rv
)) return rv
;
796 mTypes
= (char **) malloc(mTypeCount
* sizeof(char *));
798 return NS_ERROR_OUT_OF_MEMORY
;
800 // now verify that each socket type has a registered socket provider.
801 for (PRUint32 i
= 0, type
= 0; i
< mTypeCount
; ++i
) {
802 // store socket types
803 if (i
== 0 && proxyType
)
804 mTypes
[i
] = PL_strdup(proxyType
);
806 mTypes
[i
] = PL_strdup(types
[type
++]);
810 return NS_ERROR_OUT_OF_MEMORY
;
812 nsCOMPtr
<nsISocketProvider
> provider
;
813 rv
= spserv
->GetSocketProvider(mTypes
[i
], getter_AddRefs(provider
));
815 NS_WARNING("no registered socket provider");
819 // note if socket type corresponds to a transparent proxy
820 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
821 if ((strcmp(mTypes
[i
], "socks") == 0) ||
822 (strcmp(mTypes
[i
], "socks4") == 0)) {
823 mProxyTransparent
= PR_TRUE
;
825 if (proxyInfo
->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
) {
826 // we want the SOCKS layer to send the hostname
827 // and port to the proxy and let it do the DNS.
828 mProxyTransparentResolvesHost
= PR_TRUE
;
837 nsSocketTransport::InitWithConnectedSocket(PRFileDesc
*fd
, const PRNetAddr
*addr
)
840 return NS_ERROR_OUT_OF_MEMORY
;
842 NS_ASSERTION(!mFD
, "already initialized");
845 PR_NetAddrToString(addr
, buf
, sizeof(buf
));
849 if (addr
->raw
.family
== PR_AF_INET
)
850 port
= addr
->inet
.port
;
852 port
= addr
->ipv6
.port
;
853 mPort
= PR_ntohs(port
);
855 memcpy(&mNetAddr
, addr
, sizeof(PRNetAddr
));
857 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
858 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
859 mState
= STATE_TRANSFERRING
;
865 // make sure new socket is non-blocking
866 PRSocketOptionData opt
;
867 opt
.option
= PR_SockOpt_Nonblocking
;
868 opt
.value
.non_blocking
= PR_TRUE
;
869 PR_SetSocketOption(mFD
, &opt
);
871 LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
872 this, mHost
.get(), mPort
));
874 // jump to InitiateSocket to get ourselves attached to the STS poll list.
875 return PostEvent(MSG_RETRY_INIT_SOCKET
);
879 nsSocketTransport::PostEvent(PRUint32 type
, nsresult status
, nsISupports
*param
)
881 LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
882 this, type
, status
, param
));
884 nsCOMPtr
<nsIRunnable
> event
= new nsSocketEvent(this, type
, status
, param
);
886 return NS_ERROR_OUT_OF_MEMORY
;
888 return gSocketTransportService
->Dispatch(event
, NS_DISPATCH_NORMAL
);
892 nsSocketTransport::SendStatus(nsresult status
)
894 LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status
));
896 nsCOMPtr
<nsITransportEventSink
> sink
;
899 nsAutoLock
lock(mLock
);
902 case STATUS_SENDING_TO
:
903 progress
= mOutput
.ByteCount();
905 case STATUS_RECEIVING_FROM
:
906 progress
= mInput
.ByteCount();
914 sink
->OnTransportStatus(this, status
, progress
, LL_MAXUINT
);
918 nsSocketTransport::ResolveHost()
920 LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
924 if (!mProxyHost
.IsEmpty()) {
925 if (!mProxyTransparent
|| mProxyTransparentResolvesHost
) {
926 // When not resolving mHost locally, we still want to ensure that
927 // it only contains valid characters. See bug 304904 for details.
928 if (!net_IsValidHostName(mHost
))
929 return NS_ERROR_UNKNOWN_HOST
;
931 if (mProxyTransparentResolvesHost
) {
932 // Name resolution is done on the server side. Just pretend
933 // client resolution is complete, this will get picked up later.
934 // since we don't need to do DNS now, we bypass the resolving
935 // step by initializing mNetAddr to an empty address, but we
936 // must keep the port. The SOCKS IO layer will use the hostname
937 // we send it when it's created, rather than the empty address
938 // we send with the connect call.
939 mState
= STATE_RESOLVING
;
940 PR_SetNetAddr(PR_IpAddrAny
, PR_AF_INET
, SocketPort(), &mNetAddr
);
941 return PostEvent(MSG_DNS_LOOKUP_COMPLETE
, NS_OK
, nsnull
);
945 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(kDNSServiceCID
, &rv
);
946 if (NS_FAILED(rv
)) return rv
;
948 mResolving
= PR_TRUE
;
950 PRUint32 dnsFlags
= 0;
951 if (mConnectionFlags
& nsSocketTransport::BYPASS_CACHE
)
952 dnsFlags
= nsIDNSService::RESOLVE_BYPASS_CACHE
;
954 rv
= dns
->AsyncResolve(SocketHost(), dnsFlags
, this, nsnull
,
955 getter_AddRefs(mDNSRequest
));
956 if (NS_SUCCEEDED(rv
)) {
957 LOG((" advancing to STATE_RESOLVING\n"));
958 mState
= STATE_RESOLVING
;
959 // only report that we are resolving if we are still resolving...
961 SendStatus(STATUS_RESOLVING
);
967 nsSocketTransport::BuildSocket(PRFileDesc
*&fd
, PRBool
&proxyTransparent
, PRBool
&usingSSL
)
969 LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
973 proxyTransparent
= PR_FALSE
;
976 if (mTypeCount
== 0) {
977 fd
= PR_OpenTCPSocket(mNetAddr
.raw
.family
);
978 rv
= fd
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
983 nsCOMPtr
<nsISocketProviderService
> spserv
=
984 do_GetService(kSocketProviderServiceCID
, &rv
);
985 if (NS_FAILED(rv
)) return rv
;
987 const char *host
= mHost
.get();
988 PRInt32 port
= (PRInt32
) mPort
;
989 const char *proxyHost
= mProxyHost
.IsEmpty() ? nsnull
: mProxyHost
.get();
990 PRInt32 proxyPort
= (PRInt32
) mProxyPort
;
991 PRUint32 proxyFlags
= 0;
994 for (i
=0; i
<mTypeCount
; ++i
) {
995 nsCOMPtr
<nsISocketProvider
> provider
;
997 LOG((" pushing io layer [%u:%s]\n", i
, mTypes
[i
]));
999 rv
= spserv
->GetSocketProvider(mTypes
[i
], getter_AddRefs(provider
));
1003 if (mProxyTransparentResolvesHost
)
1004 proxyFlags
|= nsISocketProvider::PROXY_RESOLVES_HOST
;
1006 nsCOMPtr
<nsISupports
> secinfo
;
1008 // if this is the first type, we'll want the
1009 // service to allocate a new socket
1010 rv
= provider
->NewSocket(mNetAddr
.raw
.family
,
1011 host
, port
, proxyHost
, proxyPort
,
1013 getter_AddRefs(secinfo
));
1015 if (NS_SUCCEEDED(rv
) && !fd
) {
1016 NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
1017 rv
= NS_ERROR_UNEXPECTED
;
1021 // the socket has already been allocated,
1022 // so we just want the service to add itself
1023 // to the stack (such as pushing an io layer)
1024 rv
= provider
->AddToSocket(mNetAddr
.raw
.family
,
1025 host
, port
, proxyHost
, proxyPort
,
1027 getter_AddRefs(secinfo
));
1033 // if the service was ssl or starttls, we want to hold onto the socket info
1034 PRBool isSSL
= (strcmp(mTypes
[i
], "ssl") == 0);
1035 if (isSSL
|| (strcmp(mTypes
[i
], "starttls") == 0)) {
1036 // remember security info and give notification callbacks to PSM...
1037 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1039 nsAutoLock
lock(mLock
);
1041 callbacks
= mCallbacks
;
1042 LOG((" [secinfo=%x callbacks=%x]\n", mSecInfo
.get(), mCallbacks
.get()));
1044 // don't call into PSM while holding mLock!!
1045 nsCOMPtr
<nsISSLSocketControl
> secCtrl(do_QueryInterface(secinfo
));
1047 secCtrl
->SetNotificationCallbacks(callbacks
);
1048 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1051 else if ((strcmp(mTypes
[i
], "socks") == 0) ||
1052 (strcmp(mTypes
[i
], "socks4") == 0)) {
1053 // since socks is transparent, any layers above
1054 // it do not have to worry about proxy stuff
1057 proxyTransparent
= PR_TRUE
;
1061 if (NS_FAILED(rv
)) {
1062 LOG((" error pushing io layer [%u:%s rv=%x]\n", i
, mTypes
[i
], rv
));
1072 nsSocketTransport::InitiateSocket()
1074 LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
1079 // find out if it is going to be ok to attach another socket to the STS.
1080 // if not then we have to wait for the STS to tell us that it is ok.
1081 // the notification is asynchronous, which means that when we could be
1082 // in a race to call AttachSocket once notified. for this reason, when
1083 // we get notified, we just re-enter this function. as a result, we are
1084 // sure to ask again before calling AttachSocket. in this way we deal
1085 // with the race condition. though it isn't the most elegant solution,
1086 // it is far simpler than trying to build a system that would guarantee
1087 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1088 // 194402 for more info.
1090 if (!gSocketTransportService
->CanAttachSocket()) {
1091 nsCOMPtr
<nsIRunnable
> event
=
1092 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET
);
1094 return NS_ERROR_OUT_OF_MEMORY
;
1095 return gSocketTransportService
->NotifyWhenCanAttachSocket(event
);
1099 // if we already have a connected socket, then just attach and return.
1102 rv
= gSocketTransportService
->AttachSocket(mFD
, this);
1103 if (NS_SUCCEEDED(rv
))
1104 mAttached
= PR_TRUE
;
1109 // create new socket fd, push io layers, etc.
1112 PRBool proxyTransparent
;
1115 rv
= BuildSocket(fd
, proxyTransparent
, usingSSL
);
1116 if (NS_FAILED(rv
)) {
1117 LOG((" BuildSocket failed [rv=%x]\n", rv
));
1123 // Make the socket non-blocking...
1124 PRSocketOptionData opt
;
1125 opt
.option
= PR_SockOpt_Nonblocking
;
1126 opt
.value
.non_blocking
= PR_TRUE
;
1127 status
= PR_SetSocketOption(fd
, &opt
);
1128 NS_ASSERTION(status
== PR_SUCCESS
, "unable to make socket non-blocking");
1130 // inform socket transport about this newly created socket...
1131 rv
= gSocketTransportService
->AttachSocket(fd
, this);
1132 if (NS_FAILED(rv
)) {
1136 mAttached
= PR_TRUE
;
1138 // assign mFD so that we can properly handle OnSocketDetached before we've
1139 // established a connection.
1141 nsAutoLock
lock(mLock
);
1144 mFDconnected
= PR_FALSE
;
1147 LOG((" advancing to STATE_CONNECTING\n"));
1148 mState
= STATE_CONNECTING
;
1149 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1150 SendStatus(STATUS_CONNECTING_TO
);
1152 #if defined(PR_LOGGING)
1153 if (LOG_ENABLED()) {
1155 PR_NetAddrToString(&mNetAddr
, buf
, sizeof(buf
));
1156 LOG((" trying address: %s\n", buf
));
1161 // Initiate the connect() to the host...
1163 status
= PR_Connect(fd
, &mNetAddr
, NS_SOCKET_CONNECT_TIMEOUT
);
1164 if (status
== PR_SUCCESS
) {
1166 // we are connected!
1168 OnSocketConnected();
1171 PRErrorCode code
= PR_GetError();
1172 #if defined(TEST_CONNECT_ERRORS)
1173 code
= RandomizeConnectError(code
);
1176 // If the PR_Connect(...) would block, then poll for a connection.
1178 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
))
1179 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1181 // If the socket is already connected, then return success...
1183 else if (PR_IS_CONNECTED_ERROR
== code
) {
1185 // we are connected!
1187 OnSocketConnected();
1189 if (mSecInfo
&& !mProxyHost
.IsEmpty() && proxyTransparent
&& usingSSL
) {
1190 // if the connection phase is finished, and the ssl layer has
1191 // been pushed, and we were proxying (transparently; ie. nothing
1192 // has to happen in the protocol layer above us), it's time for
1193 // the ssl to start doing it's thing.
1194 nsCOMPtr
<nsISSLSocketControl
> secCtrl
=
1195 do_QueryInterface(mSecInfo
);
1197 LOG((" calling ProxyStartSSL()\n"));
1198 secCtrl
->ProxyStartSSL();
1200 // XXX what if we were forced to poll on the socket for a successful
1201 // connection... wouldn't we need to call ProxyStartSSL after a call
1202 // to PR_ConnectContinue indicates that we are connected?
1204 // XXX this appears to be what the old socket transport did. why
1205 // isn't this broken?
1209 // The connection was refused...
1212 rv
= ErrorAccordingToNSPR(code
);
1213 if ((rv
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty())
1214 rv
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1221 nsSocketTransport::RecoverFromError()
1223 NS_ASSERTION(NS_FAILED(mCondition
), "there should be something wrong");
1225 LOG(("nsSocketTransport::RecoverFromError [this=%x state=%x cond=%x]\n",
1226 this, mState
, mCondition
));
1228 // can only recover from errors in these states
1229 if (mState
!= STATE_RESOLVING
&& mState
!= STATE_CONNECTING
)
1232 // OK to check this outside mLock
1233 NS_ASSERTION(!mFDconnected
, "socket should not be connected");
1235 // can only recover from these errors
1236 if (mCondition
!= NS_ERROR_CONNECTION_REFUSED
&&
1237 mCondition
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
1238 mCondition
!= NS_ERROR_NET_TIMEOUT
&&
1239 mCondition
!= NS_ERROR_UNKNOWN_HOST
&&
1240 mCondition
!= NS_ERROR_UNKNOWN_PROXY_HOST
)
1243 PRBool tryAgain
= PR_FALSE
;
1245 // try next ip address only if past the resolver stage...
1246 if (mState
== STATE_CONNECTING
&& mDNSRecord
) {
1247 nsresult rv
= mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1248 if (NS_SUCCEEDED(rv
)) {
1249 LOG((" trying again with next ip address\n"));
1254 #if defined(XP_WIN) && !defined(WINCE)
1255 // If not trying next address, try to make a connection using dialup.
1256 // Retry if that connection is made.
1258 PRBool autodialEnabled
;
1259 gSocketTransportService
->GetAutodialEnabled(&autodialEnabled
);
1260 if (autodialEnabled
) {
1261 tryAgain
= nsNativeConnectionHelper::OnConnectionFailed(
1262 NS_ConvertUTF8toUTF16(SocketHost()).get());
1267 // prepare to try again.
1272 if (mState
== STATE_CONNECTING
) {
1273 mState
= STATE_RESOLVING
;
1274 msg
= MSG_DNS_LOOKUP_COMPLETE
;
1277 mState
= STATE_CLOSED
;
1278 msg
= MSG_ENSURE_CONNECT
;
1281 rv
= PostEvent(msg
, NS_OK
);
1283 tryAgain
= PR_FALSE
;
1289 // called on the socket thread only
1291 nsSocketTransport::OnMsgInputClosed(nsresult reason
)
1293 LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
1296 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
1298 mInputClosed
= PR_TRUE
;
1299 // check if event should affect entire transport
1300 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
))
1301 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1302 else if (mOutputClosed
)
1303 mCondition
= NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1305 if (mState
== STATE_TRANSFERRING
)
1306 mPollFlags
&= ~PR_POLL_READ
;
1307 mInput
.OnSocketReady(reason
);
1311 // called on the socket thread only
1313 nsSocketTransport::OnMsgOutputClosed(nsresult reason
)
1315 LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
1318 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
1320 mOutputClosed
= PR_TRUE
;
1321 // check if event should affect entire transport
1322 if (NS_FAILED(reason
) && (reason
!= NS_BASE_STREAM_CLOSED
))
1323 mCondition
= reason
; // XXX except if NS_FAILED(mCondition), right??
1324 else if (mInputClosed
)
1325 mCondition
= NS_BASE_STREAM_CLOSED
; // XXX except if NS_FAILED(mCondition), right??
1327 if (mState
== STATE_TRANSFERRING
)
1328 mPollFlags
&= ~PR_POLL_WRITE
;
1329 mOutput
.OnSocketReady(reason
);
1334 nsSocketTransport::OnSocketConnected()
1336 LOG((" advancing to STATE_TRANSFERRING\n"));
1338 mPollFlags
= (PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
);
1339 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1340 mState
= STATE_TRANSFERRING
;
1342 // assign mFD (must do this within the transport lock), but take care not
1343 // to trample over mFDref if mFD is already set.
1345 nsAutoLock
lock(mLock
);
1346 NS_ASSERTION(mFD
, "no socket");
1347 NS_ASSERTION(mFDref
== 1, "wrong socket ref count");
1348 mFDconnected
= PR_TRUE
;
1351 SendStatus(STATUS_CONNECTED_TO
);
1355 nsSocketTransport::GetFD_Locked()
1357 // mFD is not available to the streams while disconnected.
1368 nsSocketTransport::ReleaseFD_Locked(PRFileDesc
*fd
)
1370 NS_ASSERTION(mFD
== fd
, "wrong fd");
1372 if (--mFDref
== 0) {
1373 LOG(("nsSocketTransport: calling PR_Close [this=%x]\n", this));
1379 //-----------------------------------------------------------------------------
1380 // socket event handler impl
1383 nsSocketTransport::OnSocketEvent(PRUint32 type
, nsresult status
, nsISupports
*param
)
1385 LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
1386 this, type
, status
, param
));
1388 if (NS_FAILED(mCondition
)) {
1389 // block event since we're apparently already dead.
1390 LOG((" blocking event [condition=%x]\n", mCondition
));
1392 // notify input/output streams in case either has a pending notify.
1394 mInput
.OnSocketReady(mCondition
);
1395 mOutput
.OnSocketReady(mCondition
);
1400 case MSG_ENSURE_CONNECT
:
1401 LOG((" MSG_ENSURE_CONNECT\n"));
1403 // ensure that we have created a socket, attached it, and have a
1406 if (mState
== STATE_CLOSED
)
1407 mCondition
= ResolveHost();
1409 LOG((" ignoring redundant event\n"));
1412 case MSG_DNS_LOOKUP_COMPLETE
:
1413 LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1416 mDNSRecord
= static_cast<nsIDNSRecord
*>(param
);
1417 mDNSRecord
->GetNextAddr(SocketPort(), &mNetAddr
);
1419 // status contains DNS lookup status
1420 if (NS_FAILED(status
)) {
1421 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1422 // proxy host is not found, so we fixup the error code.
1423 // For SOCKS proxies (mProxyTransparent == true), the socket
1424 // transport resolves the real host here, so there's no fixup
1425 // (see bug 226943).
1426 if ((status
== NS_ERROR_UNKNOWN_HOST
) && !mProxyTransparent
&&
1427 !mProxyHost
.IsEmpty())
1428 mCondition
= NS_ERROR_UNKNOWN_PROXY_HOST
;
1430 mCondition
= status
;
1432 else if (mState
== STATE_RESOLVING
)
1433 mCondition
= InitiateSocket();
1436 case MSG_RETRY_INIT_SOCKET
:
1437 mCondition
= InitiateSocket();
1440 case MSG_INPUT_CLOSED
:
1441 LOG((" MSG_INPUT_CLOSED\n"));
1442 OnMsgInputClosed(status
);
1445 case MSG_INPUT_PENDING
:
1446 LOG((" MSG_INPUT_PENDING\n"));
1447 OnMsgInputPending();
1450 case MSG_OUTPUT_CLOSED
:
1451 LOG((" MSG_OUTPUT_CLOSED\n"));
1452 OnMsgOutputClosed(status
);
1455 case MSG_OUTPUT_PENDING
:
1456 LOG((" MSG_OUTPUT_PENDING\n"));
1457 OnMsgOutputPending();
1459 case MSG_TIMEOUT_CHANGED
:
1460 LOG((" MSG_TIMEOUT_CHANGED\n"));
1461 mPollTimeout
= mTimeouts
[(mState
== STATE_TRANSFERRING
)
1462 ? TIMEOUT_READ_WRITE
: TIMEOUT_CONNECT
];
1465 LOG((" unhandled event!\n"));
1468 if (NS_FAILED(mCondition
)) {
1469 LOG((" after event [this=%x cond=%x]\n", this, mCondition
));
1470 if (!mAttached
) // need to process this error ourselves...
1471 OnSocketDetached(nsnull
);
1473 else if (mPollFlags
== PR_POLL_EXCEPT
)
1474 mPollFlags
= 0; // make idle
1477 //-----------------------------------------------------------------------------
1478 // socket handler impl
1481 nsSocketTransport::OnSocketReady(PRFileDesc
*fd
, PRInt16 outFlags
)
1483 LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
1486 if (outFlags
== -1) {
1487 LOG(("socket timeout expired\n"));
1488 mCondition
= NS_ERROR_NET_TIMEOUT
;
1492 if (mState
== STATE_TRANSFERRING
) {
1493 // if waiting to write and socket is writable or hit an exception.
1494 if ((mPollFlags
& PR_POLL_WRITE
) && (outFlags
& ~PR_POLL_READ
)) {
1495 // assume that we won't need to poll any longer (the stream will
1496 // request that we poll again if it is still pending).
1497 mPollFlags
&= ~PR_POLL_WRITE
;
1498 mOutput
.OnSocketReady(NS_OK
);
1500 // if waiting to read and socket is readable or hit an exception.
1501 if ((mPollFlags
& PR_POLL_READ
) && (outFlags
& ~PR_POLL_WRITE
)) {
1502 // assume that we won't need to poll any longer (the stream will
1503 // request that we poll again if it is still pending).
1504 mPollFlags
&= ~PR_POLL_READ
;
1505 mInput
.OnSocketReady(NS_OK
);
1507 // Update poll timeout in case it was changed
1508 mPollTimeout
= mTimeouts
[TIMEOUT_READ_WRITE
];
1510 else if (mState
== STATE_CONNECTING
) {
1511 PRStatus status
= PR_ConnectContinue(fd
, outFlags
);
1512 if (status
== PR_SUCCESS
) {
1514 // we are connected!
1516 OnSocketConnected();
1519 PRErrorCode code
= PR_GetError();
1520 #if defined(TEST_CONNECT_ERRORS)
1521 code
= RandomizeConnectError(code
);
1524 // If the connect is still not ready, then continue polling...
1526 if ((PR_WOULD_BLOCK_ERROR
== code
) || (PR_IN_PROGRESS_ERROR
== code
)) {
1527 // Set up the select flags for connect...
1528 mPollFlags
= (PR_POLL_EXCEPT
| PR_POLL_WRITE
);
1529 // Update poll timeout in case it was changed
1530 mPollTimeout
= mTimeouts
[TIMEOUT_CONNECT
];
1534 // else, the connection failed...
1536 mCondition
= ErrorAccordingToNSPR(code
);
1537 if ((mCondition
== NS_ERROR_CONNECTION_REFUSED
) && !mProxyHost
.IsEmpty())
1538 mCondition
= NS_ERROR_PROXY_CONNECTION_REFUSED
;
1539 LOG((" connection failed! [reason=%x]\n", mCondition
));
1544 NS_ERROR("unexpected socket state");
1545 mCondition
= NS_ERROR_UNEXPECTED
;
1548 if (mPollFlags
== PR_POLL_EXCEPT
)
1549 mPollFlags
= 0; // make idle
1552 // called on the socket thread only
1554 nsSocketTransport::OnSocketDetached(PRFileDesc
*fd
)
1556 LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
1559 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread
, "wrong thread");
1561 // if we didn't initiate this detach, then be sure to pass an error
1562 // condition up to our consumers. (e.g., STS is shutting down.)
1563 if (NS_SUCCEEDED(mCondition
))
1564 mCondition
= NS_ERROR_ABORT
;
1566 if (RecoverFromError())
1569 mState
= STATE_CLOSED
;
1571 // make sure there isn't any pending DNS request
1573 mDNSRequest
->Cancel(NS_ERROR_ABORT
);
1578 // notify input/output streams
1580 mInput
.OnSocketReady(mCondition
);
1581 mOutput
.OnSocketReady(mCondition
);
1584 // break any potential reference cycle between the security info object
1585 // and ourselves by resetting its notification callbacks object. see
1586 // bug 285991 for details.
1587 nsCOMPtr
<nsISSLSocketControl
> secCtrl
= do_QueryInterface(mSecInfo
);
1589 secCtrl
->SetNotificationCallbacks(nsnull
);
1591 // finally, release our reference to the socket (must do this within
1592 // the transport lock) possibly closing the socket. Also release our
1593 // listeners to break potential refcount cycles.
1595 nsAutoLock
lock(mLock
);
1597 ReleaseFD_Locked(mFD
);
1598 // flag mFD as unusable; this prevents other consumers from
1599 // acquiring a reference to mFD.
1600 mFDconnected
= PR_FALSE
;
1603 // We must release mCallbacks and mEventSink to avoid memory leak
1604 // but only when RecoverFromError() above failed. Otherwise we lose
1605 // link with UI and security callbacks on next connection attempt
1606 // round. That would lead e.g. to a broken certificate exception page.
1607 if (NS_FAILED(mCondition
)) {
1608 mCallbacks
= nsnull
;
1609 mEventSink
= nsnull
;
1614 //-----------------------------------------------------------------------------
1617 NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport
,
1622 NS_IMPL_CI_INTERFACE_GETTER3(nsSocketTransport
,
1628 nsSocketTransport::OpenInputStream(PRUint32 flags
,
1631 nsIInputStream
**result
)
1633 LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
1636 NS_ENSURE_TRUE(!mInput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
1639 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
1641 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
1642 // XXX if the caller wants blocking, then the caller also gets buffered!
1643 //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
1644 PRBool openBlocking
= (flags
& OPEN_BLOCKING
);
1646 net_ResolveSegmentParams(segsize
, segcount
);
1647 nsIMemory
*segalloc
= net_GetSegmentAlloc(segsize
);
1650 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
1651 rv
= NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
),
1652 !openBlocking
, PR_TRUE
, segsize
, segcount
, segalloc
);
1653 if (NS_FAILED(rv
)) return rv
;
1655 // async copy from socket to pipe
1656 rv
= NS_AsyncCopy(&mInput
, pipeOut
, gSocketTransportService
,
1657 NS_ASYNCCOPY_VIA_WRITESEGMENTS
, segsize
);
1658 if (NS_FAILED(rv
)) return rv
;
1665 // flag input stream as open
1666 mInputClosed
= PR_FALSE
;
1668 rv
= PostEvent(MSG_ENSURE_CONNECT
);
1669 if (NS_FAILED(rv
)) return rv
;
1676 nsSocketTransport::OpenOutputStream(PRUint32 flags
,
1679 nsIOutputStream
**result
)
1681 LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
1684 NS_ENSURE_TRUE(!mOutput
.IsReferenced(), NS_ERROR_UNEXPECTED
);
1687 nsCOMPtr
<nsIAsyncOutputStream
> pipeOut
;
1688 if (!(flags
& OPEN_UNBUFFERED
) || (flags
& OPEN_BLOCKING
)) {
1689 // XXX if the caller wants blocking, then the caller also gets buffered!
1690 //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
1691 PRBool openBlocking
= (flags
& OPEN_BLOCKING
);
1693 net_ResolveSegmentParams(segsize
, segcount
);
1694 nsIMemory
*segalloc
= net_GetSegmentAlloc(segsize
);
1697 nsCOMPtr
<nsIAsyncInputStream
> pipeIn
;
1698 rv
= NS_NewPipe2(getter_AddRefs(pipeIn
), getter_AddRefs(pipeOut
),
1699 PR_TRUE
, !openBlocking
, segsize
, segcount
, segalloc
);
1700 if (NS_FAILED(rv
)) return rv
;
1702 // async copy from socket to pipe
1703 rv
= NS_AsyncCopy(pipeIn
, &mOutput
, gSocketTransportService
,
1704 NS_ASYNCCOPY_VIA_READSEGMENTS
, segsize
);
1705 if (NS_FAILED(rv
)) return rv
;
1712 // flag output stream as open
1713 mOutputClosed
= PR_FALSE
;
1715 rv
= PostEvent(MSG_ENSURE_CONNECT
);
1716 if (NS_FAILED(rv
)) return rv
;
1723 nsSocketTransport::Close(nsresult reason
)
1725 if (NS_SUCCEEDED(reason
))
1726 reason
= NS_BASE_STREAM_CLOSED
;
1728 mInput
.CloseWithStatus(reason
);
1729 mOutput
.CloseWithStatus(reason
);
1734 nsSocketTransport::GetSecurityInfo(nsISupports
**secinfo
)
1736 nsAutoLock
lock(mLock
);
1737 NS_IF_ADDREF(*secinfo
= mSecInfo
);
1742 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor
**callbacks
)
1744 nsAutoLock
lock(mLock
);
1745 NS_IF_ADDREF(*callbacks
= mCallbacks
);
1750 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor
*callbacks
)
1752 nsAutoLock
lock(mLock
);
1753 mCallbacks
= callbacks
;
1754 // XXX should we tell PSM about this?
1759 nsSocketTransport::SetEventSink(nsITransportEventSink
*sink
,
1760 nsIEventTarget
*target
)
1762 nsCOMPtr
<nsITransportEventSink
> temp
;
1764 nsresult rv
= net_NewTransportEventSinkProxy(getter_AddRefs(temp
),
1771 nsAutoLock
lock(mLock
);
1777 nsSocketTransport::IsAlive(PRBool
*result
)
1783 nsAutoLock
lock(mLock
);
1784 if (NS_FAILED(mCondition
))
1786 fd
= GetFD_Locked();
1791 // XXX do some idle-time based checks??
1794 PRInt32 rval
= PR_Recv(fd
, &c
, 1, PR_MSG_PEEK
, 0);
1796 if ((rval
> 0) || (rval
< 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR
))
1800 nsAutoLock
lock(mLock
);
1801 ReleaseFD_Locked(fd
);
1807 nsSocketTransport::GetHost(nsACString
&host
)
1809 host
= SocketHost();
1814 nsSocketTransport::GetPort(PRInt32
*port
)
1816 *port
= (PRInt32
) SocketPort();
1821 nsSocketTransport::GetPeerAddr(PRNetAddr
*addr
)
1823 // once we are in the connected state, mNetAddr will not change.
1824 // so if we can verify that we are in the connected state, then
1825 // we can freely access mNetAddr from any thread without being
1826 // inside a critical section.
1828 NS_ENSURE_TRUE(mState
== STATE_TRANSFERRING
, NS_ERROR_NOT_AVAILABLE
);
1830 memcpy(addr
, &mNetAddr
, sizeof(mNetAddr
));
1835 nsSocketTransport::GetSelfAddr(PRNetAddr
*addr
)
1837 // we must not call any PR methods on our file descriptor
1838 // while holding mLock since those methods might re-enter
1839 // socket transport code.
1843 nsAutoLock
lock(mLock
);
1844 fd
= GetFD_Locked();
1848 return NS_ERROR_NOT_CONNECTED
;
1851 (PR_GetSockName(fd
, addr
) == PR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
1854 nsAutoLock
lock(mLock
);
1855 ReleaseFD_Locked(fd
);
1862 nsSocketTransport::GetTimeout(PRUint32 type
, PRUint32
*value
)
1864 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
1865 *value
= (PRUint32
) mTimeouts
[type
];
1870 nsSocketTransport::SetTimeout(PRUint32 type
, PRUint32 value
)
1872 NS_ENSURE_ARG_MAX(type
, nsISocketTransport::TIMEOUT_READ_WRITE
);
1873 // truncate overly large timeout values.
1874 mTimeouts
[type
] = (PRUint16
) PR_MIN(value
, PR_UINT16_MAX
);
1875 PostEvent(MSG_TIMEOUT_CHANGED
);
1880 nsSocketTransport::OnLookupComplete(nsICancelable
*request
,
1884 // flag host lookup complete for the benefit of the ResolveHost method.
1885 mResolving
= PR_FALSE
;
1887 nsresult rv
= PostEvent(MSG_DNS_LOOKUP_COMPLETE
, status
, rec
);
1889 // if posting a message fails, then we should assume that the socket
1890 // transport has been shutdown. this should never happen! if it does
1891 // it means that the socket transport service was shutdown before the
1894 NS_WARNING("unable to post DNS lookup complete message");
1900 nsSocketTransport::GetInterfaces(PRUint32
*count
, nsIID
* **array
)
1902 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport
)(count
, array
);
1906 nsSocketTransport::GetHelperForLanguage(PRUint32 language
, nsISupports
**_retval
)
1913 nsSocketTransport::GetContractID(char * *aContractID
)
1915 *aContractID
= nsnull
;
1920 nsSocketTransport::GetClassDescription(char * *aClassDescription
)
1922 *aClassDescription
= nsnull
;
1927 nsSocketTransport::GetClassID(nsCID
* *aClassID
)
1934 nsSocketTransport::GetImplementationLanguage(PRUint32
*aImplementationLanguage
)
1936 *aImplementationLanguage
= nsIProgrammingLanguage::CPLUSPLUS
;
1941 nsSocketTransport::GetFlags(PRUint32
*aFlags
)
1943 *aFlags
= nsIClassInfo::THREADSAFE
;
1948 nsSocketTransport::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
)
1950 return NS_ERROR_NOT_AVAILABLE
;
1955 nsSocketTransport::GetConnectionFlags(PRUint32
*value
)
1957 *value
= mConnectionFlags
;
1962 nsSocketTransport::SetConnectionFlags(PRUint32 value
)
1964 mConnectionFlags
= value
;
1969 #ifdef ENABLE_SOCKET_TRACING
1976 DumpBytesToFile(const char *path
, const char *header
, const char *buf
, PRInt32 n
)
1978 FILE *fp
= fopen(path
, "a");
1980 fprintf(fp
, "\n%s [%d bytes]\n", header
, n
);
1982 const unsigned char *p
;
1984 p
= (const unsigned char *) buf
;
1986 PRInt32 i
, row_max
= PR_MIN(16, n
);
1988 for (i
= 0; i
< row_max
; ++i
)
1989 fprintf(fp
, "%02x ", *p
++);
1990 for (i
= row_max
; i
< 16; ++i
)
1993 p
= (const unsigned char *) buf
;
1994 for (i
= 0; i
< row_max
; ++i
, ++p
) {
1996 fprintf(fp
, "%c", *p
);
2011 nsSocketTransport::TraceInBuf(const char *buf
, PRInt32 n
)
2013 char *val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2017 nsCAutoString header
;
2018 header
.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost
);
2020 header
.AppendInt(mPort
);
2022 DumpBytesToFile(val
, header
.get(), buf
, n
);
2026 nsSocketTransport::TraceOutBuf(const char *buf
, PRInt32 n
)
2028 char *val
= PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2032 nsCAutoString header
;
2033 header
.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost
);
2035 header
.AppendInt(mPort
);
2037 DumpBytesToFile(val
, header
.get(), buf
, n
);