Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsSocketTransport2.cpp
bloba9f5d23bf88a370506ec58287deb600e9d9f6015
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
41 #ifdef MOZ_LOGGING
42 #define FORCE_PR_LOG
43 #endif
45 #include "nsSocketTransport2.h"
46 #include "nsIOService.h"
47 #include "nsStreamUtils.h"
48 #include "nsNetSegmentUtils.h"
49 #include "nsTransportUtils.h"
50 #include "nsProxyInfo.h"
51 #include "nsNetCID.h"
52 #include "nsAutoLock.h"
53 #include "nsAutoPtr.h"
54 #include "nsCOMPtr.h"
55 #include "netCore.h"
56 #include "nsInt64.h"
57 #include "prmem.h"
58 #include "pratom.h"
59 #include "plstr.h"
60 #include "prnetdb.h"
61 #include "prerror.h"
62 #include "prerr.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"
70 #include "nsIPipe.h"
71 #include "nsIProgrammingLanguage.h"
72 #include "nsIClassInfoImpl.h"
74 #if defined(XP_WIN)
75 #include "nsNativeConnectionHelper.h"
76 #endif
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
87 public:
88 nsSocketEvent(nsSocketTransport *transport, PRUint32 type,
89 nsresult status = NS_OK, nsISupports *param = nsnull)
90 : mTransport(transport)
91 , mType(type)
92 , mStatus(status)
93 , mParam(param)
96 NS_IMETHOD Run()
98 mTransport->OnSocketEvent(mType, mStatus, mParam);
99 return NS_OK;
102 private:
103 nsRefPtr<nsSocketTransport> mTransport;
105 PRUint32 mType;
106 nsresult mStatus;
107 nsCOMPtr<nsISupports> mParam;
110 //-----------------------------------------------------------------------------
112 //#define TEST_CONNECT_ERRORS
113 #ifdef TEST_CONNECT_ERRORS
114 #include <stdlib.h>
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.
121 int n = rand();
122 if (n > RAND_MAX/2) {
123 struct {
124 PRErrorCode err_code;
125 const char *err_name;
127 errors[] = {
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));
145 return code;
147 #endif
149 //-----------------------------------------------------------------------------
151 static PRBool
152 IsNSSErrorCode(PRErrorCode code)
154 return
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...
165 static nsresult
166 GetXPCOMFromNSSError(PRErrorCode code)
168 return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY, -1 * code);
171 static nsresult
172 ErrorAccordingToNSPR(PRErrorCode errorCode)
174 nsresult rv = NS_ERROR_FAILURE;
175 switch (errorCode) {
176 case PR_WOULD_BLOCK_ERROR:
177 rv = NS_BASE_STREAM_WOULD_BLOCK;
178 break;
179 case PR_CONNECT_ABORTED_ERROR:
180 case PR_CONNECT_RESET_ERROR:
181 rv = NS_ERROR_NET_RESET;
182 break;
183 case PR_END_OF_FILE_ERROR: // XXX document this correlation
184 rv = NS_ERROR_NET_INTERRUPT;
185 break;
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;
195 break;
196 case PR_IO_TIMEOUT_ERROR:
197 case PR_CONNECT_TIMEOUT_ERROR:
198 rv = NS_ERROR_NET_TIMEOUT;
199 break;
200 default:
201 if (IsNSSErrorCode(errorCode))
202 rv = GetXPCOMFromNSSError(errorCode);
203 break;
205 LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
206 return rv;
209 //-----------------------------------------------------------------------------
210 // socket input stream impl
211 //-----------------------------------------------------------------------------
213 nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
214 : mTransport(trans)
215 , mReaderRefCnt(0)
216 , mCondition(NS_OK)
217 , mCallbackFlags(0)
218 , mByteCount(0)
222 nsSocketInputStream::~nsSocketInputStream()
226 // called on the socket transport thread...
228 // condition : failure code if socket has been closed
230 void
231 nsSocketInputStream::OnSocketReady(nsresult condition)
233 LOG(("nsSocketInputStream::OnSocketReady [this=%x cond=%x]\n",
234 this, condition));
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;
250 mCallback = nsnull;
251 mCallbackFlags = 0;
255 if (callback)
256 callback->OnInputStreamReady(this);
259 NS_IMPL_QUERY_INTERFACE2(nsSocketInputStream,
260 nsIInputStream,
261 nsIAsyncInputStream)
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)
274 Close();
275 return mTransport->Release();
278 NS_IMETHODIMP
279 nsSocketInputStream::Close()
281 return CloseWithStatus(NS_BASE_STREAM_CLOSED);
284 NS_IMETHODIMP
285 nsSocketInputStream::Available(PRUint32 *avail)
287 LOG(("nsSocketInputStream::Available [this=%x]\n", this));
289 *avail = 0;
291 PRFileDesc *fd;
293 nsAutoLock lock(mTransport->mLock);
295 if (NS_FAILED(mCondition))
296 return mCondition;
298 fd = mTransport->GetFD_Locked();
299 if (!fd)
300 return NS_OK;
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);
308 nsresult rv;
310 nsAutoLock lock(mTransport->mLock);
312 mTransport->ReleaseFD_Locked(fd);
314 if (n >= 0)
315 *avail = n;
316 else {
317 PRErrorCode code = PR_GetError();
318 if (code == PR_WOULD_BLOCK_ERROR)
319 return NS_OK;
320 mCondition = ErrorAccordingToNSPR(code);
322 rv = mCondition;
324 if (NS_FAILED(rv))
325 mTransport->OnInputClosed(rv);
326 return rv;
329 NS_IMETHODIMP
330 nsSocketInputStream::Read(char *buf, PRUint32 count, PRUint32 *countRead)
332 LOG(("nsSocketInputStream::Read [this=%x count=%u]\n", this, count));
334 *countRead = 0;
336 PRFileDesc *fd;
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();
344 if (!fd)
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));
357 nsresult rv;
359 nsAutoLock lock(mTransport->mLock);
361 #ifdef ENABLE_SOCKET_TRACING
362 if (n > 0)
363 mTransport->TraceInBuf(buf, n);
364 #endif
366 mTransport->ReleaseFD_Locked(fd);
368 if (n > 0)
369 mByteCount += (*countRead = n);
370 else if (n < 0) {
371 PRErrorCode code = PR_GetError();
372 if (code == PR_WOULD_BLOCK_ERROR)
373 return NS_BASE_STREAM_WOULD_BLOCK;
374 mCondition = ErrorAccordingToNSPR(code);
376 rv = mCondition;
378 if (NS_FAILED(rv))
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.
383 if (n > 0)
384 mTransport->SendStatus(nsISocketTransport::STATUS_RECEIVING_FROM);
385 return rv;
388 NS_IMETHODIMP
389 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
390 PRUint32 count, PRUint32 *countRead)
392 // socket stream is unbuffered
393 return NS_ERROR_NOT_IMPLEMENTED;
396 NS_IMETHODIMP
397 nsSocketInputStream::IsNonBlocking(PRBool *nonblocking)
399 *nonblocking = PR_TRUE;
400 return NS_OK;
403 NS_IMETHODIMP
404 nsSocketInputStream::CloseWithStatus(nsresult reason)
406 LOG(("nsSocketInputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason));
408 // may be called from any thread
410 nsresult rv;
412 nsAutoLock lock(mTransport->mLock);
414 if (NS_SUCCEEDED(mCondition))
415 rv = mCondition = reason;
416 else
417 rv = NS_OK;
419 if (NS_FAILED(rv))
420 mTransport->OnInputClosed(rv);
421 return NS_OK;
424 NS_IMETHODIMP
425 nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
426 PRUint32 flags,
427 PRUint32 amount,
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) {
441 // build event proxy
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),
448 callback, target);
449 if (NS_FAILED(rv)) return rv;
450 mCallback = temp;
452 else
453 mCallback = callback;
455 if (NS_FAILED(mCondition))
456 directCallback.swap(mCallback);
457 else
458 mCallbackFlags = flags;
460 if (directCallback)
461 directCallback->OnInputStreamReady(this);
462 else
463 mTransport->OnInputPending();
465 return NS_OK;
468 //-----------------------------------------------------------------------------
469 // socket output stream impl
470 //-----------------------------------------------------------------------------
472 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
473 : mTransport(trans)
474 , mWriterRefCnt(0)
475 , mCondition(NS_OK)
476 , mCallbackFlags(0)
477 , mByteCount(0)
481 nsSocketOutputStream::~nsSocketOutputStream()
485 // called on the socket transport thread...
487 // condition : failure code if socket has been closed
489 void
490 nsSocketOutputStream::OnSocketReady(nsresult condition)
492 LOG(("nsSocketOutputStream::OnSocketReady [this=%x cond=%x]\n",
493 this, condition));
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;
509 mCallback = nsnull;
510 mCallbackFlags = 0;
514 if (callback)
515 callback->OnOutputStreamReady(this);
518 NS_IMPL_QUERY_INTERFACE2(nsSocketOutputStream,
519 nsIOutputStream,
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)
533 Close();
534 return mTransport->Release();
537 NS_IMETHODIMP
538 nsSocketOutputStream::Close()
540 return CloseWithStatus(NS_BASE_STREAM_CLOSED);
543 NS_IMETHODIMP
544 nsSocketOutputStream::Flush()
546 return NS_OK;
549 NS_IMETHODIMP
550 nsSocketOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *countWritten)
552 LOG(("nsSocketOutputStream::Write [this=%x count=%u]\n", this, count));
554 *countWritten = 0;
556 if (count == 0)
557 return NS_OK;
559 PRFileDesc *fd;
561 nsAutoLock lock(mTransport->mLock);
563 if (NS_FAILED(mCondition))
564 return mCondition;
566 fd = mTransport->GetFD_Locked();
567 if (!fd)
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");
581 nsresult rv;
583 nsAutoLock lock(mTransport->mLock);
585 #ifdef ENABLE_SOCKET_TRACING
586 if (n > 0)
587 mTransport->TraceOutBuf(buf, n);
588 #endif
590 mTransport->ReleaseFD_Locked(fd);
592 if (n > 0)
593 mByteCount += (*countWritten = n);
594 else if (n < 0) {
595 PRErrorCode code = PR_GetError();
596 if (code == PR_WOULD_BLOCK_ERROR)
597 return NS_BASE_STREAM_WOULD_BLOCK;
598 mCondition = ErrorAccordingToNSPR(code);
600 rv = mCondition;
602 if (NS_FAILED(rv))
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.
607 if (n > 0)
608 mTransport->SendStatus(nsISocketTransport::STATUS_SENDING_TO);
609 return rv;
612 NS_IMETHODIMP
613 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
614 PRUint32 count, PRUint32 *countRead)
616 // socket stream is unbuffered
617 return NS_ERROR_NOT_IMPLEMENTED;
620 NS_METHOD
621 nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
622 void *closure,
623 const char *fromSegment,
624 PRUint32 offset,
625 PRUint32 count,
626 PRUint32 *countRead)
628 nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
629 return self->Write(fromSegment, count, countRead);
632 NS_IMETHODIMP
633 nsSocketOutputStream::WriteFrom(nsIInputStream *stream, PRUint32 count, PRUint32 *countRead)
635 return stream->ReadSegments(WriteFromSegments, this, count, countRead);
638 NS_IMETHODIMP
639 nsSocketOutputStream::IsNonBlocking(PRBool *nonblocking)
641 *nonblocking = PR_TRUE;
642 return NS_OK;
645 NS_IMETHODIMP
646 nsSocketOutputStream::CloseWithStatus(nsresult reason)
648 LOG(("nsSocketOutputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason));
650 // may be called from any thread
652 nsresult rv;
654 nsAutoLock lock(mTransport->mLock);
656 if (NS_SUCCEEDED(mCondition))
657 rv = mCondition = reason;
658 else
659 rv = NS_OK;
661 if (NS_FAILED(rv))
662 mTransport->OnOutputClosed(rv);
663 return NS_OK;
666 NS_IMETHODIMP
667 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
668 PRUint32 flags,
669 PRUint32 amount,
670 nsIEventTarget *target)
672 LOG(("nsSocketOutputStream::AsyncWait [this=%x]\n", this));
675 nsAutoLock lock(mTransport->mLock);
677 if (callback && target) {
679 // build event proxy
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),
686 callback, target);
687 if (NS_FAILED(rv)) return rv;
688 mCallback = temp;
690 else
691 mCallback = callback;
693 mCallbackFlags = flags;
695 mTransport->OnOutputPending();
696 return NS_OK;
699 //-----------------------------------------------------------------------------
700 // socket transport impl
701 //-----------------------------------------------------------------------------
703 nsSocketTransport::nsSocketTransport()
704 : mTypes(nsnull)
705 , mTypeCount(0)
706 , mPort(0)
707 , mProxyPort(0)
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())
717 , mFD(nsnull)
718 , mFDref(0)
719 , mFDconnected(PR_FALSE)
720 , mInput(this)
721 , mOutput(this)
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
736 if (mTypes) {
737 PRUint32 i;
738 for (i=0; i<mTypeCount; ++i)
739 PL_strfree(mTypes[i]);
740 free(mTypes);
743 if (mLock)
744 PR_DestroyLock(mLock);
746 nsSocketTransportService *serv = gSocketTransportService;
747 NS_RELEASE(serv); // nulls argument
750 nsresult
751 nsSocketTransport::Init(const char **types, PRUint32 typeCount,
752 const nsACString &host, PRUint16 port,
753 nsIProxyInfo *givenProxyInfo)
755 if (!mLock)
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
766 mPort = port;
767 mHost = host;
769 const char *proxyType = nsnull;
770 if (proxyInfo) {
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))
778 proxyType = nsnull;
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);
786 if (!mTypeCount)
787 return NS_OK;
789 // if we have socket types, then the socket provider service had
790 // better exist!
791 nsresult rv;
792 nsCOMPtr<nsISocketProviderService> spserv =
793 do_GetService(kSocketProviderServiceCID, &rv);
794 if (NS_FAILED(rv)) return rv;
796 mTypes = (char **) malloc(mTypeCount * sizeof(char *));
797 if (!mTypes)
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);
805 else
806 mTypes[i] = PL_strdup(types[type++]);
808 if (!mTypes[i]) {
809 mTypeCount = i;
810 return NS_ERROR_OUT_OF_MEMORY;
812 nsCOMPtr<nsISocketProvider> provider;
813 rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
814 if (NS_FAILED(rv)) {
815 NS_WARNING("no registered socket provider");
816 return rv;
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;
833 return NS_OK;
836 nsresult
837 nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const PRNetAddr *addr)
839 if (!mLock)
840 return NS_ERROR_OUT_OF_MEMORY;
842 NS_ASSERTION(!mFD, "already initialized");
844 char buf[64];
845 PR_NetAddrToString(addr, buf, sizeof(buf));
846 mHost.Assign(buf);
848 PRUint16 port;
849 if (addr->raw.family == PR_AF_INET)
850 port = addr->inet.port;
851 else
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;
861 mFD = fd;
862 mFDref = 1;
863 mFDconnected = 1;
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);
878 nsresult
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);
885 if (!event)
886 return NS_ERROR_OUT_OF_MEMORY;
888 return gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
891 void
892 nsSocketTransport::SendStatus(nsresult status)
894 LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status));
896 nsCOMPtr<nsITransportEventSink> sink;
897 PRUint64 progress;
899 nsAutoLock lock(mLock);
900 sink = mEventSink;
901 switch (status) {
902 case STATUS_SENDING_TO:
903 progress = mOutput.ByteCount();
904 break;
905 case STATUS_RECEIVING_FROM:
906 progress = mInput.ByteCount();
907 break;
908 default:
909 progress = 0;
910 break;
913 if (sink)
914 sink->OnTransportStatus(this, status, progress, LL_MAXUINT);
917 nsresult
918 nsSocketTransport::ResolveHost()
920 LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
922 nsresult rv;
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...
960 if (mResolving)
961 SendStatus(STATUS_RESOLVING);
963 return rv;
966 nsresult
967 nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool &usingSSL)
969 LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
971 nsresult rv;
973 proxyTransparent = PR_FALSE;
974 usingSSL = PR_FALSE;
976 if (mTypeCount == 0) {
977 fd = PR_OpenTCPSocket(mNetAddr.raw.family);
978 rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
980 else {
981 fd = nsnull;
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;
993 PRUint32 i;
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));
1000 if (NS_FAILED(rv))
1001 break;
1003 if (mProxyTransparentResolvesHost)
1004 proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
1006 nsCOMPtr<nsISupports> secinfo;
1007 if (i == 0) {
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,
1012 proxyFlags, &fd,
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;
1020 else {
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,
1026 proxyFlags, fd,
1027 getter_AddRefs(secinfo));
1029 proxyFlags = 0;
1030 if (NS_FAILED(rv))
1031 break;
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);
1040 mSecInfo = secinfo;
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));
1046 if (secCtrl)
1047 secCtrl->SetNotificationCallbacks(callbacks);
1048 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1049 usingSSL = isSSL;
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
1055 proxyHost = nsnull;
1056 proxyPort = -1;
1057 proxyTransparent = PR_TRUE;
1061 if (NS_FAILED(rv)) {
1062 LOG((" error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
1063 if (fd)
1064 PR_Close(fd);
1068 return rv;
1071 nsresult
1072 nsSocketTransport::InitiateSocket()
1074 LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
1076 nsresult rv;
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);
1093 if (!event)
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.
1101 if (mFD) {
1102 rv = gSocketTransportService->AttachSocket(mFD, this);
1103 if (NS_SUCCEEDED(rv))
1104 mAttached = PR_TRUE;
1105 return rv;
1109 // create new socket fd, push io layers, etc.
1111 PRFileDesc *fd;
1112 PRBool proxyTransparent;
1113 PRBool usingSSL;
1115 rv = BuildSocket(fd, proxyTransparent, usingSSL);
1116 if (NS_FAILED(rv)) {
1117 LOG((" BuildSocket failed [rv=%x]\n", rv));
1118 return rv;
1121 PRStatus status;
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)) {
1133 PR_Close(fd);
1134 return 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);
1142 mFD = fd;
1143 mFDref = 1;
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()) {
1154 char buf[64];
1155 PR_NetAddrToString(&mNetAddr, buf, sizeof(buf));
1156 LOG((" trying address: %s\n", buf));
1158 #endif
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();
1170 else {
1171 PRErrorCode code = PR_GetError();
1172 #if defined(TEST_CONNECT_ERRORS)
1173 code = RandomizeConnectError(code);
1174 #endif
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);
1196 if (secCtrl) {
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...
1211 else {
1212 rv = ErrorAccordingToNSPR(code);
1213 if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
1214 rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1217 return rv;
1220 PRBool
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)
1230 return PR_FALSE;
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)
1241 return PR_FALSE;
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"));
1250 tryAgain = PR_TRUE;
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.
1257 if (!tryAgain) {
1258 PRBool autodialEnabled;
1259 gSocketTransportService->GetAutodialEnabled(&autodialEnabled);
1260 if (autodialEnabled) {
1261 tryAgain = nsNativeConnectionHelper::OnConnectionFailed(
1262 NS_ConvertUTF8toUTF16(SocketHost()).get());
1265 #endif
1267 // prepare to try again.
1268 if (tryAgain) {
1269 nsresult rv;
1270 PRUint32 msg;
1272 if (mState == STATE_CONNECTING) {
1273 mState = STATE_RESOLVING;
1274 msg = MSG_DNS_LOOKUP_COMPLETE;
1276 else {
1277 mState = STATE_CLOSED;
1278 msg = MSG_ENSURE_CONNECT;
1281 rv = PostEvent(msg, NS_OK);
1282 if (NS_FAILED(rv))
1283 tryAgain = PR_FALSE;
1286 return tryAgain;
1289 // called on the socket thread only
1290 void
1291 nsSocketTransport::OnMsgInputClosed(nsresult reason)
1293 LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
1294 this, reason));
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??
1304 else {
1305 if (mState == STATE_TRANSFERRING)
1306 mPollFlags &= ~PR_POLL_READ;
1307 mInput.OnSocketReady(reason);
1311 // called on the socket thread only
1312 void
1313 nsSocketTransport::OnMsgOutputClosed(nsresult reason)
1315 LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
1316 this, reason));
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??
1326 else {
1327 if (mState == STATE_TRANSFERRING)
1328 mPollFlags &= ~PR_POLL_WRITE;
1329 mOutput.OnSocketReady(reason);
1333 void
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);
1354 PRFileDesc *
1355 nsSocketTransport::GetFD_Locked()
1357 // mFD is not available to the streams while disconnected.
1358 if (!mFDconnected)
1359 return nsnull;
1361 if (mFD)
1362 mFDref++;
1364 return mFD;
1367 void
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));
1374 PR_Close(mFD);
1375 mFD = nsnull;
1379 //-----------------------------------------------------------------------------
1380 // socket event handler impl
1382 void
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);
1396 return;
1399 switch (type) {
1400 case MSG_ENSURE_CONNECT:
1401 LOG((" MSG_ENSURE_CONNECT\n"));
1403 // ensure that we have created a socket, attached it, and have a
1404 // connection.
1406 if (mState == STATE_CLOSED)
1407 mCondition = ResolveHost();
1408 else
1409 LOG((" ignoring redundant event\n"));
1410 break;
1412 case MSG_DNS_LOOKUP_COMPLETE:
1413 LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1414 mDNSRequest = 0;
1415 if (param) {
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;
1429 else
1430 mCondition = status;
1432 else if (mState == STATE_RESOLVING)
1433 mCondition = InitiateSocket();
1434 break;
1436 case MSG_RETRY_INIT_SOCKET:
1437 mCondition = InitiateSocket();
1438 break;
1440 case MSG_INPUT_CLOSED:
1441 LOG((" MSG_INPUT_CLOSED\n"));
1442 OnMsgInputClosed(status);
1443 break;
1445 case MSG_INPUT_PENDING:
1446 LOG((" MSG_INPUT_PENDING\n"));
1447 OnMsgInputPending();
1448 break;
1450 case MSG_OUTPUT_CLOSED:
1451 LOG((" MSG_OUTPUT_CLOSED\n"));
1452 OnMsgOutputClosed(status);
1453 break;
1455 case MSG_OUTPUT_PENDING:
1456 LOG((" MSG_OUTPUT_PENDING\n"));
1457 OnMsgOutputPending();
1458 break;
1459 case MSG_TIMEOUT_CHANGED:
1460 LOG((" MSG_TIMEOUT_CHANGED\n"));
1461 mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
1462 ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
1463 break;
1464 default:
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
1480 void
1481 nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
1483 LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
1484 this, outFlags));
1486 if (outFlags == -1) {
1487 LOG(("socket timeout expired\n"));
1488 mCondition = NS_ERROR_NET_TIMEOUT;
1489 return;
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();
1518 else {
1519 PRErrorCode code = PR_GetError();
1520 #if defined(TEST_CONNECT_ERRORS)
1521 code = RandomizeConnectError(code);
1522 #endif
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];
1532 else {
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));
1543 else {
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
1553 void
1554 nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
1556 LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
1557 this, mCondition));
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())
1567 mCondition = NS_OK;
1568 else {
1569 mState = STATE_CLOSED;
1571 // make sure there isn't any pending DNS request
1572 if (mDNSRequest) {
1573 mDNSRequest->Cancel(NS_ERROR_ABORT);
1574 mDNSRequest = 0;
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);
1588 if (secCtrl)
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);
1596 if (mFD) {
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 //-----------------------------------------------------------------------------
1615 // xpcom api
1617 NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport,
1618 nsISocketTransport,
1619 nsITransport,
1620 nsIDNSListener,
1621 nsIClassInfo)
1622 NS_IMPL_CI_INTERFACE_GETTER3(nsSocketTransport,
1623 nsISocketTransport,
1624 nsITransport,
1625 nsIDNSListener)
1627 NS_IMETHODIMP
1628 nsSocketTransport::OpenInputStream(PRUint32 flags,
1629 PRUint32 segsize,
1630 PRUint32 segcount,
1631 nsIInputStream **result)
1633 LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
1634 this, flags));
1636 NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
1638 nsresult rv;
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);
1649 // create a pipe
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;
1660 *result = pipeIn;
1662 else
1663 *result = &mInput;
1665 // flag input stream as open
1666 mInputClosed = PR_FALSE;
1668 rv = PostEvent(MSG_ENSURE_CONNECT);
1669 if (NS_FAILED(rv)) return rv;
1671 NS_ADDREF(*result);
1672 return NS_OK;
1675 NS_IMETHODIMP
1676 nsSocketTransport::OpenOutputStream(PRUint32 flags,
1677 PRUint32 segsize,
1678 PRUint32 segcount,
1679 nsIOutputStream **result)
1681 LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
1682 this, flags));
1684 NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
1686 nsresult rv;
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);
1696 // create a pipe
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;
1707 *result = pipeOut;
1709 else
1710 *result = &mOutput;
1712 // flag output stream as open
1713 mOutputClosed = PR_FALSE;
1715 rv = PostEvent(MSG_ENSURE_CONNECT);
1716 if (NS_FAILED(rv)) return rv;
1718 NS_ADDREF(*result);
1719 return NS_OK;
1722 NS_IMETHODIMP
1723 nsSocketTransport::Close(nsresult reason)
1725 if (NS_SUCCEEDED(reason))
1726 reason = NS_BASE_STREAM_CLOSED;
1728 mInput.CloseWithStatus(reason);
1729 mOutput.CloseWithStatus(reason);
1730 return NS_OK;
1733 NS_IMETHODIMP
1734 nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
1736 nsAutoLock lock(mLock);
1737 NS_IF_ADDREF(*secinfo = mSecInfo);
1738 return NS_OK;
1741 NS_IMETHODIMP
1742 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
1744 nsAutoLock lock(mLock);
1745 NS_IF_ADDREF(*callbacks = mCallbacks);
1746 return NS_OK;
1749 NS_IMETHODIMP
1750 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
1752 nsAutoLock lock(mLock);
1753 mCallbacks = callbacks;
1754 // XXX should we tell PSM about this?
1755 return NS_OK;
1758 NS_IMETHODIMP
1759 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
1760 nsIEventTarget *target)
1762 nsCOMPtr<nsITransportEventSink> temp;
1763 if (target) {
1764 nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
1765 sink, target);
1766 if (NS_FAILED(rv))
1767 return rv;
1768 sink = temp.get();
1771 nsAutoLock lock(mLock);
1772 mEventSink = sink;
1773 return NS_OK;
1776 NS_IMETHODIMP
1777 nsSocketTransport::IsAlive(PRBool *result)
1779 *result = PR_FALSE;
1781 PRFileDesc *fd;
1783 nsAutoLock lock(mLock);
1784 if (NS_FAILED(mCondition))
1785 return NS_OK;
1786 fd = GetFD_Locked();
1787 if (!fd)
1788 return NS_OK;
1791 // XXX do some idle-time based checks??
1793 char c;
1794 PRInt32 rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
1796 if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
1797 *result = PR_TRUE;
1800 nsAutoLock lock(mLock);
1801 ReleaseFD_Locked(fd);
1803 return NS_OK;
1806 NS_IMETHODIMP
1807 nsSocketTransport::GetHost(nsACString &host)
1809 host = SocketHost();
1810 return NS_OK;
1813 NS_IMETHODIMP
1814 nsSocketTransport::GetPort(PRInt32 *port)
1816 *port = (PRInt32) SocketPort();
1817 return NS_OK;
1820 NS_IMETHODIMP
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));
1831 return NS_OK;
1834 NS_IMETHODIMP
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.
1841 PRFileDesc *fd;
1843 nsAutoLock lock(mLock);
1844 fd = GetFD_Locked();
1847 if (!fd)
1848 return NS_ERROR_NOT_CONNECTED;
1850 nsresult rv =
1851 (PR_GetSockName(fd, addr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
1854 nsAutoLock lock(mLock);
1855 ReleaseFD_Locked(fd);
1858 return rv;
1861 NS_IMETHODIMP
1862 nsSocketTransport::GetTimeout(PRUint32 type, PRUint32 *value)
1864 NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
1865 *value = (PRUint32) mTimeouts[type];
1866 return NS_OK;
1869 NS_IMETHODIMP
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);
1876 return NS_OK;
1879 NS_IMETHODIMP
1880 nsSocketTransport::OnLookupComplete(nsICancelable *request,
1881 nsIDNSRecord *rec,
1882 nsresult status)
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
1892 // DNS service.
1893 if (NS_FAILED(rv))
1894 NS_WARNING("unable to post DNS lookup complete message");
1896 return NS_OK;
1899 NS_IMETHODIMP
1900 nsSocketTransport::GetInterfaces(PRUint32 *count, nsIID * **array)
1902 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
1905 NS_IMETHODIMP
1906 nsSocketTransport::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
1908 *_retval = nsnull;
1909 return NS_OK;
1912 NS_IMETHODIMP
1913 nsSocketTransport::GetContractID(char * *aContractID)
1915 *aContractID = nsnull;
1916 return NS_OK;
1919 NS_IMETHODIMP
1920 nsSocketTransport::GetClassDescription(char * *aClassDescription)
1922 *aClassDescription = nsnull;
1923 return NS_OK;
1926 NS_IMETHODIMP
1927 nsSocketTransport::GetClassID(nsCID * *aClassID)
1929 *aClassID = nsnull;
1930 return NS_OK;
1933 NS_IMETHODIMP
1934 nsSocketTransport::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
1936 *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
1937 return NS_OK;
1940 NS_IMETHODIMP
1941 nsSocketTransport::GetFlags(PRUint32 *aFlags)
1943 *aFlags = nsIClassInfo::THREADSAFE;
1944 return NS_OK;
1947 NS_IMETHODIMP
1948 nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
1950 return NS_ERROR_NOT_AVAILABLE;
1954 NS_IMETHODIMP
1955 nsSocketTransport::GetConnectionFlags(PRUint32 *value)
1957 *value = mConnectionFlags;
1958 return NS_OK;
1961 NS_IMETHODIMP
1962 nsSocketTransport::SetConnectionFlags(PRUint32 value)
1964 mConnectionFlags = value;
1965 return NS_OK;
1969 #ifdef ENABLE_SOCKET_TRACING
1971 #include <stdio.h>
1972 #include <ctype.h>
1973 #include "prenv.h"
1975 static void
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;
1983 while (n) {
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)
1991 fprintf(fp, " ");
1993 p = (const unsigned char *) buf;
1994 for (i = 0; i < row_max; ++i, ++p) {
1995 if (isprint(*p))
1996 fprintf(fp, "%c", *p);
1997 else
1998 fprintf(fp, ".");
2001 fprintf(fp, "\n");
2002 buf += row_max;
2003 n -= row_max;
2006 fprintf(fp, "\n");
2007 fclose(fp);
2010 void
2011 nsSocketTransport::TraceInBuf(const char *buf, PRInt32 n)
2013 char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2014 if (!val || !*val)
2015 return;
2017 nsCAutoString header;
2018 header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
2019 header.Append(':');
2020 header.AppendInt(mPort);
2022 DumpBytesToFile(val, header.get(), buf, n);
2025 void
2026 nsSocketTransport::TraceOutBuf(const char *buf, PRInt32 n)
2028 char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2029 if (!val || !*val)
2030 return;
2032 nsCAutoString header;
2033 header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
2034 header.Append(':');
2035 header.AppendInt(mPort);
2037 DumpBytesToFile(val, header.get(), buf, n);
2040 #endif