On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / netwerk / base / src / nsSocketTransport2.cpp
blob0c83db9cbcacb82de264041523b03439c1c64b2c
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 , mState(STATE_CLOSED)
711 , mAttached(PR_FALSE)
712 , mInputClosed(PR_TRUE)
713 , mOutputClosed(PR_TRUE)
714 , mResolving(PR_FALSE)
715 , mLock(PR_NewLock())
716 , mFD(nsnull)
717 , mFDref(0)
718 , mFDconnected(PR_FALSE)
719 , mInput(this)
720 , mOutput(this)
722 LOG(("creating nsSocketTransport @%x\n", this));
724 NS_ADDREF(gSocketTransportService);
726 mTimeouts[TIMEOUT_CONNECT] = PR_UINT16_MAX; // no timeout
727 mTimeouts[TIMEOUT_READ_WRITE] = PR_UINT16_MAX; // no timeout
730 nsSocketTransport::~nsSocketTransport()
732 LOG(("destroying nsSocketTransport @%x\n", this));
734 // cleanup socket type info
735 if (mTypes) {
736 PRUint32 i;
737 for (i=0; i<mTypeCount; ++i)
738 PL_strfree(mTypes[i]);
739 free(mTypes);
742 if (mLock)
743 PR_DestroyLock(mLock);
745 nsSocketTransportService *serv = gSocketTransportService;
746 NS_RELEASE(serv); // nulls argument
749 nsresult
750 nsSocketTransport::Init(const char **types, PRUint32 typeCount,
751 const nsACString &host, PRUint16 port,
752 nsIProxyInfo *givenProxyInfo)
754 if (!mLock)
755 return NS_ERROR_OUT_OF_MEMORY;
757 nsCOMPtr<nsProxyInfo> proxyInfo;
758 if (givenProxyInfo) {
759 proxyInfo = do_QueryInterface(givenProxyInfo);
760 NS_ENSURE_ARG(proxyInfo);
763 // init socket type info
765 mPort = port;
766 mHost = host;
768 const char *proxyType = nsnull;
769 if (proxyInfo) {
770 mProxyPort = proxyInfo->Port();
771 mProxyHost = proxyInfo->Host();
772 // grab proxy type (looking for "socks" for example)
773 proxyType = proxyInfo->Type();
774 if (proxyType && (strcmp(proxyType, "http") == 0 ||
775 strcmp(proxyType, "direct") == 0 ||
776 strcmp(proxyType, "unknown") == 0))
777 proxyType = nsnull;
780 LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s:%hu]\n",
781 this, mHost.get(), mPort, mProxyHost.get(), mProxyPort));
783 // include proxy type as a socket type if proxy type is not "http"
784 mTypeCount = typeCount + (proxyType != nsnull);
785 if (!mTypeCount)
786 return NS_OK;
788 // if we have socket types, then the socket provider service had
789 // better exist!
790 nsresult rv;
791 nsCOMPtr<nsISocketProviderService> spserv =
792 do_GetService(kSocketProviderServiceCID, &rv);
793 if (NS_FAILED(rv)) return rv;
795 mTypes = (char **) malloc(mTypeCount * sizeof(char *));
796 if (!mTypes)
797 return NS_ERROR_OUT_OF_MEMORY;
799 // now verify that each socket type has a registered socket provider.
800 for (PRUint32 i = 0, type = 0; i < mTypeCount; ++i) {
801 // store socket types
802 if (i == 0 && proxyType)
803 mTypes[i] = PL_strdup(proxyType);
804 else
805 mTypes[i] = PL_strdup(types[type++]);
807 if (!mTypes[i]) {
808 mTypeCount = i;
809 return NS_ERROR_OUT_OF_MEMORY;
811 nsCOMPtr<nsISocketProvider> provider;
812 rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
813 if (NS_FAILED(rv)) {
814 NS_WARNING("no registered socket provider");
815 return rv;
818 // note if socket type corresponds to a transparent proxy
819 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
820 if ((strcmp(mTypes[i], "socks") == 0) ||
821 (strcmp(mTypes[i], "socks4") == 0)) {
822 mProxyTransparent = PR_TRUE;
824 if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
825 // we want the SOCKS layer to send the hostname
826 // and port to the proxy and let it do the DNS.
827 mProxyTransparentResolvesHost = PR_TRUE;
832 return NS_OK;
835 nsresult
836 nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const PRNetAddr *addr)
838 if (!mLock)
839 return NS_ERROR_OUT_OF_MEMORY;
841 NS_ASSERTION(!mFD, "already initialized");
843 char buf[64];
844 PR_NetAddrToString(addr, buf, sizeof(buf));
845 mHost.Assign(buf);
847 PRUint16 port;
848 if (addr->raw.family == PR_AF_INET)
849 port = addr->inet.port;
850 else
851 port = addr->ipv6.port;
852 mPort = PR_ntohs(port);
854 memcpy(&mNetAddr, addr, sizeof(PRNetAddr));
856 mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
857 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
858 mState = STATE_TRANSFERRING;
860 mFD = fd;
861 mFDref = 1;
862 mFDconnected = 1;
864 // make sure new socket is non-blocking
865 PRSocketOptionData opt;
866 opt.option = PR_SockOpt_Nonblocking;
867 opt.value.non_blocking = PR_TRUE;
868 PR_SetSocketOption(mFD, &opt);
870 LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
871 this, mHost.get(), mPort));
873 // jump to InitiateSocket to get ourselves attached to the STS poll list.
874 return PostEvent(MSG_RETRY_INIT_SOCKET);
877 nsresult
878 nsSocketTransport::PostEvent(PRUint32 type, nsresult status, nsISupports *param)
880 LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
881 this, type, status, param));
883 nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
884 if (!event)
885 return NS_ERROR_OUT_OF_MEMORY;
887 return gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
890 void
891 nsSocketTransport::SendStatus(nsresult status)
893 LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status));
895 nsCOMPtr<nsITransportEventSink> sink;
896 PRUint64 progress;
898 nsAutoLock lock(mLock);
899 sink = mEventSink;
900 switch (status) {
901 case STATUS_SENDING_TO:
902 progress = mOutput.ByteCount();
903 break;
904 case STATUS_RECEIVING_FROM:
905 progress = mInput.ByteCount();
906 break;
907 default:
908 progress = 0;
909 break;
912 if (sink)
913 sink->OnTransportStatus(this, status, progress, LL_MAXUINT);
916 nsresult
917 nsSocketTransport::ResolveHost()
919 LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
921 nsresult rv;
923 if (!mProxyHost.IsEmpty()) {
924 if (!mProxyTransparent || mProxyTransparentResolvesHost) {
925 // When not resolving mHost locally, we still want to ensure that
926 // it only contains valid characters. See bug 304904 for details.
927 if (!net_IsValidHostName(mHost))
928 return NS_ERROR_UNKNOWN_HOST;
930 if (mProxyTransparentResolvesHost) {
931 // Name resolution is done on the server side. Just pretend
932 // client resolution is complete, this will get picked up later.
933 // since we don't need to do DNS now, we bypass the resolving
934 // step by initializing mNetAddr to an empty address, but we
935 // must keep the port. The SOCKS IO layer will use the hostname
936 // we send it when it's created, rather than the empty address
937 // we send with the connect call.
938 mState = STATE_RESOLVING;
939 PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, SocketPort(), &mNetAddr);
940 return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nsnull);
944 nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
945 if (NS_FAILED(rv)) return rv;
947 mResolving = PR_TRUE;
949 rv = dns->AsyncResolve(SocketHost(), 0, this, nsnull,
950 getter_AddRefs(mDNSRequest));
951 if (NS_SUCCEEDED(rv)) {
952 LOG((" advancing to STATE_RESOLVING\n"));
953 mState = STATE_RESOLVING;
954 // only report that we are resolving if we are still resolving...
955 if (mResolving)
956 SendStatus(STATUS_RESOLVING);
958 return rv;
961 nsresult
962 nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool &usingSSL)
964 LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
966 nsresult rv;
968 proxyTransparent = PR_FALSE;
969 usingSSL = PR_FALSE;
971 if (mTypeCount == 0) {
972 fd = PR_OpenTCPSocket(mNetAddr.raw.family);
973 rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
975 else {
976 fd = nsnull;
978 nsCOMPtr<nsISocketProviderService> spserv =
979 do_GetService(kSocketProviderServiceCID, &rv);
980 if (NS_FAILED(rv)) return rv;
982 const char *host = mHost.get();
983 PRInt32 port = (PRInt32) mPort;
984 const char *proxyHost = mProxyHost.IsEmpty() ? nsnull : mProxyHost.get();
985 PRInt32 proxyPort = (PRInt32) mProxyPort;
986 PRUint32 proxyFlags = 0;
988 PRUint32 i;
989 for (i=0; i<mTypeCount; ++i) {
990 nsCOMPtr<nsISocketProvider> provider;
992 LOG((" pushing io layer [%u:%s]\n", i, mTypes[i]));
994 rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
995 if (NS_FAILED(rv))
996 break;
998 if (mProxyTransparentResolvesHost)
999 proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
1001 nsCOMPtr<nsISupports> secinfo;
1002 if (i == 0) {
1003 // if this is the first type, we'll want the
1004 // service to allocate a new socket
1005 rv = provider->NewSocket(mNetAddr.raw.family,
1006 host, port, proxyHost, proxyPort,
1007 proxyFlags, &fd,
1008 getter_AddRefs(secinfo));
1010 if (NS_SUCCEEDED(rv) && !fd) {
1011 NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
1012 rv = NS_ERROR_UNEXPECTED;
1015 else {
1016 // the socket has already been allocated,
1017 // so we just want the service to add itself
1018 // to the stack (such as pushing an io layer)
1019 rv = provider->AddToSocket(mNetAddr.raw.family,
1020 host, port, proxyHost, proxyPort,
1021 proxyFlags, fd,
1022 getter_AddRefs(secinfo));
1024 proxyFlags = 0;
1025 if (NS_FAILED(rv))
1026 break;
1028 // if the service was ssl or starttls, we want to hold onto the socket info
1029 PRBool isSSL = (strcmp(mTypes[i], "ssl") == 0);
1030 if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
1031 // remember security info and give notification callbacks to PSM...
1032 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1034 nsAutoLock lock(mLock);
1035 mSecInfo = secinfo;
1036 callbacks = mCallbacks;
1037 LOG((" [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
1039 // don't call into PSM while holding mLock!!
1040 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
1041 if (secCtrl)
1042 secCtrl->SetNotificationCallbacks(callbacks);
1043 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1044 usingSSL = isSSL;
1046 else if ((strcmp(mTypes[i], "socks") == 0) ||
1047 (strcmp(mTypes[i], "socks4") == 0)) {
1048 // since socks is transparent, any layers above
1049 // it do not have to worry about proxy stuff
1050 proxyHost = nsnull;
1051 proxyPort = -1;
1052 proxyTransparent = PR_TRUE;
1056 if (NS_FAILED(rv)) {
1057 LOG((" error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
1058 if (fd)
1059 PR_Close(fd);
1063 return rv;
1066 nsresult
1067 nsSocketTransport::InitiateSocket()
1069 LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
1071 nsresult rv;
1074 // find out if it is going to be ok to attach another socket to the STS.
1075 // if not then we have to wait for the STS to tell us that it is ok.
1076 // the notification is asynchronous, which means that when we could be
1077 // in a race to call AttachSocket once notified. for this reason, when
1078 // we get notified, we just re-enter this function. as a result, we are
1079 // sure to ask again before calling AttachSocket. in this way we deal
1080 // with the race condition. though it isn't the most elegant solution,
1081 // it is far simpler than trying to build a system that would guarantee
1082 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1083 // 194402 for more info.
1085 if (!gSocketTransportService->CanAttachSocket()) {
1086 nsCOMPtr<nsIRunnable> event =
1087 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
1088 if (!event)
1089 return NS_ERROR_OUT_OF_MEMORY;
1090 return gSocketTransportService->NotifyWhenCanAttachSocket(event);
1094 // if we already have a connected socket, then just attach and return.
1096 if (mFD) {
1097 rv = gSocketTransportService->AttachSocket(mFD, this);
1098 if (NS_SUCCEEDED(rv))
1099 mAttached = PR_TRUE;
1100 return rv;
1104 // create new socket fd, push io layers, etc.
1106 PRFileDesc *fd;
1107 PRBool proxyTransparent;
1108 PRBool usingSSL;
1110 rv = BuildSocket(fd, proxyTransparent, usingSSL);
1111 if (NS_FAILED(rv)) {
1112 LOG((" BuildSocket failed [rv=%x]\n", rv));
1113 return rv;
1116 PRStatus status;
1118 // Make the socket non-blocking...
1119 PRSocketOptionData opt;
1120 opt.option = PR_SockOpt_Nonblocking;
1121 opt.value.non_blocking = PR_TRUE;
1122 status = PR_SetSocketOption(fd, &opt);
1123 NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
1125 // inform socket transport about this newly created socket...
1126 rv = gSocketTransportService->AttachSocket(fd, this);
1127 if (NS_FAILED(rv)) {
1128 PR_Close(fd);
1129 return rv;
1131 mAttached = PR_TRUE;
1133 // assign mFD so that we can properly handle OnSocketDetached before we've
1134 // established a connection.
1136 nsAutoLock lock(mLock);
1137 mFD = fd;
1138 mFDref = 1;
1139 mFDconnected = PR_FALSE;
1142 LOG((" advancing to STATE_CONNECTING\n"));
1143 mState = STATE_CONNECTING;
1144 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1145 SendStatus(STATUS_CONNECTING_TO);
1147 #if defined(PR_LOGGING)
1148 if (LOG_ENABLED()) {
1149 char buf[64];
1150 PR_NetAddrToString(&mNetAddr, buf, sizeof(buf));
1151 LOG((" trying address: %s\n", buf));
1153 #endif
1156 // Initiate the connect() to the host...
1158 status = PR_Connect(fd, &mNetAddr, NS_SOCKET_CONNECT_TIMEOUT);
1159 if (status == PR_SUCCESS) {
1161 // we are connected!
1163 OnSocketConnected();
1165 else {
1166 PRErrorCode code = PR_GetError();
1167 #if defined(TEST_CONNECT_ERRORS)
1168 code = RandomizeConnectError(code);
1169 #endif
1171 // If the PR_Connect(...) would block, then poll for a connection.
1173 if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
1174 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1176 // If the socket is already connected, then return success...
1178 else if (PR_IS_CONNECTED_ERROR == code) {
1180 // we are connected!
1182 OnSocketConnected();
1184 if (mSecInfo && !mProxyHost.IsEmpty() && proxyTransparent && usingSSL) {
1185 // if the connection phase is finished, and the ssl layer has
1186 // been pushed, and we were proxying (transparently; ie. nothing
1187 // has to happen in the protocol layer above us), it's time for
1188 // the ssl to start doing it's thing.
1189 nsCOMPtr<nsISSLSocketControl> secCtrl =
1190 do_QueryInterface(mSecInfo);
1191 if (secCtrl) {
1192 LOG((" calling ProxyStartSSL()\n"));
1193 secCtrl->ProxyStartSSL();
1195 // XXX what if we were forced to poll on the socket for a successful
1196 // connection... wouldn't we need to call ProxyStartSSL after a call
1197 // to PR_ConnectContinue indicates that we are connected?
1199 // XXX this appears to be what the old socket transport did. why
1200 // isn't this broken?
1204 // The connection was refused...
1206 else {
1207 rv = ErrorAccordingToNSPR(code);
1208 if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
1209 rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1212 return rv;
1215 PRBool
1216 nsSocketTransport::RecoverFromError()
1218 NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
1220 LOG(("nsSocketTransport::RecoverFromError [this=%x state=%x cond=%x]\n",
1221 this, mState, mCondition));
1223 // can only recover from errors in these states
1224 if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
1225 return PR_FALSE;
1227 // OK to check this outside mLock
1228 NS_ASSERTION(!mFDconnected, "socket should not be connected");
1230 // can only recover from these errors
1231 if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
1232 mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
1233 mCondition != NS_ERROR_NET_TIMEOUT &&
1234 mCondition != NS_ERROR_UNKNOWN_HOST &&
1235 mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
1236 return PR_FALSE;
1238 PRBool tryAgain = PR_FALSE;
1240 // try next ip address only if past the resolver stage...
1241 if (mState == STATE_CONNECTING && mDNSRecord) {
1242 nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1243 if (NS_SUCCEEDED(rv)) {
1244 LOG((" trying again with next ip address\n"));
1245 tryAgain = PR_TRUE;
1249 #if defined(XP_WIN) && !defined(WINCE)
1250 // If not trying next address, try to make a connection using dialup.
1251 // Retry if that connection is made.
1252 if (!tryAgain) {
1253 PRBool autodialEnabled;
1254 gSocketTransportService->GetAutodialEnabled(&autodialEnabled);
1255 if (autodialEnabled) {
1256 tryAgain = nsNativeConnectionHelper::OnConnectionFailed(
1257 NS_ConvertUTF8toUTF16(SocketHost()).get());
1260 #endif
1262 // prepare to try again.
1263 if (tryAgain) {
1264 nsresult rv;
1265 PRUint32 msg;
1267 if (mState == STATE_CONNECTING) {
1268 mState = STATE_RESOLVING;
1269 msg = MSG_DNS_LOOKUP_COMPLETE;
1271 else {
1272 mState = STATE_CLOSED;
1273 msg = MSG_ENSURE_CONNECT;
1276 rv = PostEvent(msg, NS_OK);
1277 if (NS_FAILED(rv))
1278 tryAgain = PR_FALSE;
1281 return tryAgain;
1284 // called on the socket thread only
1285 void
1286 nsSocketTransport::OnMsgInputClosed(nsresult reason)
1288 LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
1289 this, reason));
1291 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1293 mInputClosed = PR_TRUE;
1294 // check if event should affect entire transport
1295 if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
1296 mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
1297 else if (mOutputClosed)
1298 mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1299 else {
1300 if (mState == STATE_TRANSFERRING)
1301 mPollFlags &= ~PR_POLL_READ;
1302 mInput.OnSocketReady(reason);
1306 // called on the socket thread only
1307 void
1308 nsSocketTransport::OnMsgOutputClosed(nsresult reason)
1310 LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
1311 this, reason));
1313 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1315 mOutputClosed = PR_TRUE;
1316 // check if event should affect entire transport
1317 if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
1318 mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
1319 else if (mInputClosed)
1320 mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1321 else {
1322 if (mState == STATE_TRANSFERRING)
1323 mPollFlags &= ~PR_POLL_WRITE;
1324 mOutput.OnSocketReady(reason);
1328 void
1329 nsSocketTransport::OnSocketConnected()
1331 LOG((" advancing to STATE_TRANSFERRING\n"));
1333 mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
1334 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1335 mState = STATE_TRANSFERRING;
1337 // assign mFD (must do this within the transport lock), but take care not
1338 // to trample over mFDref if mFD is already set.
1340 nsAutoLock lock(mLock);
1341 NS_ASSERTION(mFD, "no socket");
1342 NS_ASSERTION(mFDref == 1, "wrong socket ref count");
1343 mFDconnected = PR_TRUE;
1346 SendStatus(STATUS_CONNECTED_TO);
1349 PRFileDesc *
1350 nsSocketTransport::GetFD_Locked()
1352 // mFD is not available to the streams while disconnected.
1353 if (!mFDconnected)
1354 return nsnull;
1356 if (mFD)
1357 mFDref++;
1359 return mFD;
1362 void
1363 nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
1365 NS_ASSERTION(mFD == fd, "wrong fd");
1367 if (--mFDref == 0) {
1368 LOG(("nsSocketTransport: calling PR_Close [this=%x]\n", this));
1369 PR_Close(mFD);
1370 mFD = nsnull;
1374 //-----------------------------------------------------------------------------
1375 // socket event handler impl
1377 void
1378 nsSocketTransport::OnSocketEvent(PRUint32 type, nsresult status, nsISupports *param)
1380 LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
1381 this, type, status, param));
1383 if (NS_FAILED(mCondition)) {
1384 // block event since we're apparently already dead.
1385 LOG((" blocking event [condition=%x]\n", mCondition));
1387 // notify input/output streams in case either has a pending notify.
1389 mInput.OnSocketReady(mCondition);
1390 mOutput.OnSocketReady(mCondition);
1391 return;
1394 switch (type) {
1395 case MSG_ENSURE_CONNECT:
1396 LOG((" MSG_ENSURE_CONNECT\n"));
1398 // ensure that we have created a socket, attached it, and have a
1399 // connection.
1401 if (mState == STATE_CLOSED)
1402 mCondition = ResolveHost();
1403 else
1404 LOG((" ignoring redundant event\n"));
1405 break;
1407 case MSG_DNS_LOOKUP_COMPLETE:
1408 LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1409 mDNSRequest = 0;
1410 if (param) {
1411 mDNSRecord = static_cast<nsIDNSRecord *>(param);
1412 mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1414 // status contains DNS lookup status
1415 if (NS_FAILED(status)) {
1416 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1417 // proxy host is not found, so we fixup the error code.
1418 // For SOCKS proxies (mProxyTransparent == true), the socket
1419 // transport resolves the real host here, so there's no fixup
1420 // (see bug 226943).
1421 if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyTransparent &&
1422 !mProxyHost.IsEmpty())
1423 mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
1424 else
1425 mCondition = status;
1427 else if (mState == STATE_RESOLVING)
1428 mCondition = InitiateSocket();
1429 break;
1431 case MSG_RETRY_INIT_SOCKET:
1432 mCondition = InitiateSocket();
1433 break;
1435 case MSG_INPUT_CLOSED:
1436 LOG((" MSG_INPUT_CLOSED\n"));
1437 OnMsgInputClosed(status);
1438 break;
1440 case MSG_INPUT_PENDING:
1441 LOG((" MSG_INPUT_PENDING\n"));
1442 OnMsgInputPending();
1443 break;
1445 case MSG_OUTPUT_CLOSED:
1446 LOG((" MSG_OUTPUT_CLOSED\n"));
1447 OnMsgOutputClosed(status);
1448 break;
1450 case MSG_OUTPUT_PENDING:
1451 LOG((" MSG_OUTPUT_PENDING\n"));
1452 OnMsgOutputPending();
1453 break;
1454 case MSG_TIMEOUT_CHANGED:
1455 LOG((" MSG_TIMEOUT_CHANGED\n"));
1456 mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
1457 ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
1458 break;
1459 default:
1460 LOG((" unhandled event!\n"));
1463 if (NS_FAILED(mCondition)) {
1464 LOG((" after event [this=%x cond=%x]\n", this, mCondition));
1465 if (!mAttached) // need to process this error ourselves...
1466 OnSocketDetached(nsnull);
1468 else if (mPollFlags == PR_POLL_EXCEPT)
1469 mPollFlags = 0; // make idle
1472 //-----------------------------------------------------------------------------
1473 // socket handler impl
1475 void
1476 nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
1478 LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
1479 this, outFlags));
1481 if (outFlags == -1) {
1482 LOG(("socket timeout expired\n"));
1483 mCondition = NS_ERROR_NET_TIMEOUT;
1484 return;
1487 if (mState == STATE_TRANSFERRING) {
1488 // if waiting to write and socket is writable or hit an exception.
1489 if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
1490 // assume that we won't need to poll any longer (the stream will
1491 // request that we poll again if it is still pending).
1492 mPollFlags &= ~PR_POLL_WRITE;
1493 mOutput.OnSocketReady(NS_OK);
1495 // if waiting to read and socket is readable or hit an exception.
1496 if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
1497 // assume that we won't need to poll any longer (the stream will
1498 // request that we poll again if it is still pending).
1499 mPollFlags &= ~PR_POLL_READ;
1500 mInput.OnSocketReady(NS_OK);
1502 // Update poll timeout in case it was changed
1503 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1505 else if (mState == STATE_CONNECTING) {
1506 PRStatus status = PR_ConnectContinue(fd, outFlags);
1507 if (status == PR_SUCCESS) {
1509 // we are connected!
1511 OnSocketConnected();
1513 else {
1514 PRErrorCode code = PR_GetError();
1515 #if defined(TEST_CONNECT_ERRORS)
1516 code = RandomizeConnectError(code);
1517 #endif
1519 // If the connect is still not ready, then continue polling...
1521 if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
1522 // Set up the select flags for connect...
1523 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1524 // Update poll timeout in case it was changed
1525 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1527 else {
1529 // else, the connection failed...
1531 mCondition = ErrorAccordingToNSPR(code);
1532 if ((mCondition == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
1533 mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
1534 LOG((" connection failed! [reason=%x]\n", mCondition));
1538 else {
1539 NS_ERROR("unexpected socket state");
1540 mCondition = NS_ERROR_UNEXPECTED;
1543 if (mPollFlags == PR_POLL_EXCEPT)
1544 mPollFlags = 0; // make idle
1547 // called on the socket thread only
1548 void
1549 nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
1551 LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
1552 this, mCondition));
1554 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1556 // if we didn't initiate this detach, then be sure to pass an error
1557 // condition up to our consumers. (e.g., STS is shutting down.)
1558 if (NS_SUCCEEDED(mCondition))
1559 mCondition = NS_ERROR_ABORT;
1561 if (RecoverFromError())
1562 mCondition = NS_OK;
1563 else {
1564 mState = STATE_CLOSED;
1566 // make sure there isn't any pending DNS request
1567 if (mDNSRequest) {
1568 mDNSRequest->Cancel(NS_ERROR_ABORT);
1569 mDNSRequest = 0;
1573 // notify input/output streams
1575 mInput.OnSocketReady(mCondition);
1576 mOutput.OnSocketReady(mCondition);
1579 // break any potential reference cycle between the security info object
1580 // and ourselves by resetting its notification callbacks object. see
1581 // bug 285991 for details.
1582 nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1583 if (secCtrl)
1584 secCtrl->SetNotificationCallbacks(nsnull);
1586 // finally, release our reference to the socket (must do this within
1587 // the transport lock) possibly closing the socket. Also release our
1588 // listeners to break potential refcount cycles.
1590 nsAutoLock lock(mLock);
1591 if (mFD) {
1592 ReleaseFD_Locked(mFD);
1593 // flag mFD as unusable; this prevents other consumers from
1594 // acquiring a reference to mFD.
1595 mFDconnected = PR_FALSE;
1597 mCallbacks = nsnull;
1598 mEventSink = nsnull;
1602 //-----------------------------------------------------------------------------
1603 // xpcom api
1605 NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport,
1606 nsISocketTransport,
1607 nsITransport,
1608 nsIDNSListener,
1609 nsIClassInfo)
1610 NS_IMPL_CI_INTERFACE_GETTER3(nsSocketTransport,
1611 nsISocketTransport,
1612 nsITransport,
1613 nsIDNSListener)
1615 NS_IMETHODIMP
1616 nsSocketTransport::OpenInputStream(PRUint32 flags,
1617 PRUint32 segsize,
1618 PRUint32 segcount,
1619 nsIInputStream **result)
1621 LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
1622 this, flags));
1624 NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
1626 nsresult rv;
1627 nsCOMPtr<nsIAsyncInputStream> pipeIn;
1629 if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
1630 // XXX if the caller wants blocking, then the caller also gets buffered!
1631 //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
1632 PRBool openBlocking = (flags & OPEN_BLOCKING);
1634 net_ResolveSegmentParams(segsize, segcount);
1635 nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
1637 // create a pipe
1638 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
1639 rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
1640 !openBlocking, PR_TRUE, segsize, segcount, segalloc);
1641 if (NS_FAILED(rv)) return rv;
1643 // async copy from socket to pipe
1644 rv = NS_AsyncCopy(&mInput, pipeOut, gSocketTransportService,
1645 NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
1646 if (NS_FAILED(rv)) return rv;
1648 *result = pipeIn;
1650 else
1651 *result = &mInput;
1653 // flag input stream as open
1654 mInputClosed = PR_FALSE;
1656 rv = PostEvent(MSG_ENSURE_CONNECT);
1657 if (NS_FAILED(rv)) return rv;
1659 NS_ADDREF(*result);
1660 return NS_OK;
1663 NS_IMETHODIMP
1664 nsSocketTransport::OpenOutputStream(PRUint32 flags,
1665 PRUint32 segsize,
1666 PRUint32 segcount,
1667 nsIOutputStream **result)
1669 LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
1670 this, flags));
1672 NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
1674 nsresult rv;
1675 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
1676 if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
1677 // XXX if the caller wants blocking, then the caller also gets buffered!
1678 //PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
1679 PRBool openBlocking = (flags & OPEN_BLOCKING);
1681 net_ResolveSegmentParams(segsize, segcount);
1682 nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
1684 // create a pipe
1685 nsCOMPtr<nsIAsyncInputStream> pipeIn;
1686 rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
1687 PR_TRUE, !openBlocking, segsize, segcount, segalloc);
1688 if (NS_FAILED(rv)) return rv;
1690 // async copy from socket to pipe
1691 rv = NS_AsyncCopy(pipeIn, &mOutput, gSocketTransportService,
1692 NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
1693 if (NS_FAILED(rv)) return rv;
1695 *result = pipeOut;
1697 else
1698 *result = &mOutput;
1700 // flag output stream as open
1701 mOutputClosed = PR_FALSE;
1703 rv = PostEvent(MSG_ENSURE_CONNECT);
1704 if (NS_FAILED(rv)) return rv;
1706 NS_ADDREF(*result);
1707 return NS_OK;
1710 NS_IMETHODIMP
1711 nsSocketTransport::Close(nsresult reason)
1713 if (NS_SUCCEEDED(reason))
1714 reason = NS_BASE_STREAM_CLOSED;
1716 mInput.CloseWithStatus(reason);
1717 mOutput.CloseWithStatus(reason);
1718 return NS_OK;
1721 NS_IMETHODIMP
1722 nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
1724 nsAutoLock lock(mLock);
1725 NS_IF_ADDREF(*secinfo = mSecInfo);
1726 return NS_OK;
1729 NS_IMETHODIMP
1730 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
1732 nsAutoLock lock(mLock);
1733 NS_IF_ADDREF(*callbacks = mCallbacks);
1734 return NS_OK;
1737 NS_IMETHODIMP
1738 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
1740 nsAutoLock lock(mLock);
1741 mCallbacks = callbacks;
1742 // XXX should we tell PSM about this?
1743 return NS_OK;
1746 NS_IMETHODIMP
1747 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
1748 nsIEventTarget *target)
1750 nsCOMPtr<nsITransportEventSink> temp;
1751 if (target) {
1752 nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
1753 sink, target);
1754 if (NS_FAILED(rv))
1755 return rv;
1756 sink = temp.get();
1759 nsAutoLock lock(mLock);
1760 mEventSink = sink;
1761 return NS_OK;
1764 NS_IMETHODIMP
1765 nsSocketTransport::IsAlive(PRBool *result)
1767 *result = PR_FALSE;
1769 PRFileDesc *fd;
1771 nsAutoLock lock(mLock);
1772 if (NS_FAILED(mCondition))
1773 return NS_OK;
1774 fd = GetFD_Locked();
1775 if (!fd)
1776 return NS_OK;
1779 // XXX do some idle-time based checks??
1781 char c;
1782 PRInt32 rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
1784 if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
1785 *result = PR_TRUE;
1788 nsAutoLock lock(mLock);
1789 ReleaseFD_Locked(fd);
1791 return NS_OK;
1794 NS_IMETHODIMP
1795 nsSocketTransport::GetHost(nsACString &host)
1797 host = SocketHost();
1798 return NS_OK;
1801 NS_IMETHODIMP
1802 nsSocketTransport::GetPort(PRInt32 *port)
1804 *port = (PRInt32) SocketPort();
1805 return NS_OK;
1808 NS_IMETHODIMP
1809 nsSocketTransport::GetPeerAddr(PRNetAddr *addr)
1811 // once we are in the connected state, mNetAddr will not change.
1812 // so if we can verify that we are in the connected state, then
1813 // we can freely access mNetAddr from any thread without being
1814 // inside a critical section.
1816 NS_ENSURE_TRUE(mState == STATE_TRANSFERRING, NS_ERROR_NOT_AVAILABLE);
1818 memcpy(addr, &mNetAddr, sizeof(mNetAddr));
1819 return NS_OK;
1822 NS_IMETHODIMP
1823 nsSocketTransport::GetSelfAddr(PRNetAddr *addr)
1825 // we must not call any PR methods on our file descriptor
1826 // while holding mLock since those methods might re-enter
1827 // socket transport code.
1829 PRFileDesc *fd;
1831 nsAutoLock lock(mLock);
1832 fd = GetFD_Locked();
1835 if (!fd)
1836 return NS_ERROR_NOT_CONNECTED;
1838 nsresult rv =
1839 (PR_GetSockName(fd, addr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
1842 nsAutoLock lock(mLock);
1843 ReleaseFD_Locked(fd);
1846 return rv;
1849 NS_IMETHODIMP
1850 nsSocketTransport::GetTimeout(PRUint32 type, PRUint32 *value)
1852 NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
1853 *value = (PRUint32) mTimeouts[type];
1854 return NS_OK;
1857 NS_IMETHODIMP
1858 nsSocketTransport::SetTimeout(PRUint32 type, PRUint32 value)
1860 NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
1861 // truncate overly large timeout values.
1862 mTimeouts[type] = (PRUint16) PR_MIN(value, PR_UINT16_MAX);
1863 PostEvent(MSG_TIMEOUT_CHANGED);
1864 return NS_OK;
1867 NS_IMETHODIMP
1868 nsSocketTransport::OnLookupComplete(nsICancelable *request,
1869 nsIDNSRecord *rec,
1870 nsresult status)
1872 // flag host lookup complete for the benefit of the ResolveHost method.
1873 mResolving = PR_FALSE;
1875 nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
1877 // if posting a message fails, then we should assume that the socket
1878 // transport has been shutdown. this should never happen! if it does
1879 // it means that the socket transport service was shutdown before the
1880 // DNS service.
1881 if (NS_FAILED(rv))
1882 NS_WARNING("unable to post DNS lookup complete message");
1884 return NS_OK;
1887 NS_IMETHODIMP
1888 nsSocketTransport::GetInterfaces(PRUint32 *count, nsIID * **array)
1890 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
1893 NS_IMETHODIMP
1894 nsSocketTransport::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
1896 *_retval = nsnull;
1897 return NS_OK;
1900 NS_IMETHODIMP
1901 nsSocketTransport::GetContractID(char * *aContractID)
1903 *aContractID = nsnull;
1904 return NS_OK;
1907 NS_IMETHODIMP
1908 nsSocketTransport::GetClassDescription(char * *aClassDescription)
1910 *aClassDescription = nsnull;
1911 return NS_OK;
1914 NS_IMETHODIMP
1915 nsSocketTransport::GetClassID(nsCID * *aClassID)
1917 *aClassID = nsnull;
1918 return NS_OK;
1921 NS_IMETHODIMP
1922 nsSocketTransport::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
1924 *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
1925 return NS_OK;
1928 NS_IMETHODIMP
1929 nsSocketTransport::GetFlags(PRUint32 *aFlags)
1931 *aFlags = nsIClassInfo::THREADSAFE;
1932 return NS_OK;
1935 NS_IMETHODIMP
1936 nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
1938 return NS_ERROR_NOT_AVAILABLE;
1942 #ifdef ENABLE_SOCKET_TRACING
1944 #include <stdio.h>
1945 #include <ctype.h>
1946 #include "prenv.h"
1948 static void
1949 DumpBytesToFile(const char *path, const char *header, const char *buf, PRInt32 n)
1951 FILE *fp = fopen(path, "a");
1953 fprintf(fp, "\n%s [%d bytes]\n", header, n);
1955 const unsigned char *p;
1956 while (n) {
1957 p = (const unsigned char *) buf;
1959 PRInt32 i, row_max = PR_MIN(16, n);
1961 for (i = 0; i < row_max; ++i)
1962 fprintf(fp, "%02x ", *p++);
1963 for (i = row_max; i < 16; ++i)
1964 fprintf(fp, " ");
1966 p = (const unsigned char *) buf;
1967 for (i = 0; i < row_max; ++i, ++p) {
1968 if (isprint(*p))
1969 fprintf(fp, "%c", *p);
1970 else
1971 fprintf(fp, ".");
1974 fprintf(fp, "\n");
1975 buf += row_max;
1976 n -= row_max;
1979 fprintf(fp, "\n");
1980 fclose(fp);
1983 void
1984 nsSocketTransport::TraceInBuf(const char *buf, PRInt32 n)
1986 char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
1987 if (!val || !*val)
1988 return;
1990 nsCAutoString header;
1991 header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
1992 header.Append(':');
1993 header.AppendInt(mPort);
1995 DumpBytesToFile(val, header.get(), buf, n);
1998 void
1999 nsSocketTransport::TraceOutBuf(const char *buf, PRInt32 n)
2001 char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2002 if (!val || !*val)
2003 return;
2005 nsCAutoString header;
2006 header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
2007 header.Append(':');
2008 header.AppendInt(mPort);
2010 DumpBytesToFile(val, header.get(), buf, n);
2013 #endif