michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsSocketTransport2_h__ michael@0: #define nsSocketTransport2_h__ michael@0: michael@0: #ifdef DEBUG_darinf michael@0: #define ENABLE_SOCKET_TRACING michael@0: #endif michael@0: michael@0: #include "mozilla/Mutex.h" michael@0: #include "nsSocketTransportService2.h" michael@0: #include "nsString.h" michael@0: #include "nsCOMPtr.h" michael@0: michael@0: #include "nsISocketTransport.h" michael@0: #include "nsIAsyncInputStream.h" michael@0: #include "nsIAsyncOutputStream.h" michael@0: #include "nsIDNSListener.h" michael@0: #include "nsIClassInfo.h" michael@0: #include "mozilla/net/DNS.h" michael@0: #include "nsASocketHandler.h" michael@0: michael@0: #include "prerror.h" michael@0: #include "nsAutoPtr.h" michael@0: michael@0: class nsSocketTransport; michael@0: class nsICancelable; michael@0: class nsIDNSRecord; michael@0: class nsIInterfaceRequestor; michael@0: michael@0: nsresult michael@0: ErrorAccordingToNSPR(PRErrorCode errorCode); michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: // after this short interval, we will return to PR_Poll michael@0: #define NS_SOCKET_CONNECT_TIMEOUT PR_MillisecondsToInterval(20) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsSocketInputStream : public nsIAsyncInputStream michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIINPUTSTREAM michael@0: NS_DECL_NSIASYNCINPUTSTREAM michael@0: michael@0: nsSocketInputStream(nsSocketTransport *); michael@0: virtual ~nsSocketInputStream(); michael@0: michael@0: bool IsReferenced() { return mReaderRefCnt > 0; } michael@0: nsresult Condition() { return mCondition; } michael@0: uint64_t ByteCount() { return mByteCount; } michael@0: michael@0: // called by the socket transport on the socket thread... michael@0: void OnSocketReady(nsresult condition); michael@0: michael@0: private: michael@0: nsSocketTransport *mTransport; michael@0: mozilla::ThreadSafeAutoRefCnt mReaderRefCnt; michael@0: michael@0: // access to these is protected by mTransport->mLock michael@0: nsresult mCondition; michael@0: nsCOMPtr mCallback; michael@0: uint32_t mCallbackFlags; michael@0: uint64_t mByteCount; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsSocketOutputStream : public nsIAsyncOutputStream michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIOUTPUTSTREAM michael@0: NS_DECL_NSIASYNCOUTPUTSTREAM michael@0: michael@0: nsSocketOutputStream(nsSocketTransport *); michael@0: virtual ~nsSocketOutputStream(); michael@0: michael@0: bool IsReferenced() { return mWriterRefCnt > 0; } michael@0: nsresult Condition() { return mCondition; } michael@0: uint64_t ByteCount() { return mByteCount; } michael@0: michael@0: // called by the socket transport on the socket thread... michael@0: void OnSocketReady(nsresult condition); michael@0: michael@0: private: michael@0: static NS_METHOD WriteFromSegments(nsIInputStream *, void *, michael@0: const char *, uint32_t offset, michael@0: uint32_t count, uint32_t *countRead); michael@0: michael@0: nsSocketTransport *mTransport; michael@0: mozilla::ThreadSafeAutoRefCnt mWriterRefCnt; michael@0: michael@0: // access to these is protected by mTransport->mLock michael@0: nsresult mCondition; michael@0: nsCOMPtr mCallback; michael@0: uint32_t mCallbackFlags; michael@0: uint64_t mByteCount; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsSocketTransport : public nsASocketHandler michael@0: , public nsISocketTransport michael@0: , public nsIDNSListener michael@0: , public nsIClassInfo michael@0: { michael@0: typedef mozilla::Mutex Mutex; michael@0: michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSITRANSPORT michael@0: NS_DECL_NSISOCKETTRANSPORT michael@0: NS_DECL_NSIDNSLISTENER michael@0: NS_DECL_NSICLASSINFO michael@0: michael@0: nsSocketTransport(); michael@0: michael@0: // this method instructs the socket transport to open a socket of the michael@0: // given type(s) to the given host or proxy. michael@0: nsresult Init(const char **socketTypes, uint32_t typeCount, michael@0: const nsACString &host, uint16_t port, michael@0: nsIProxyInfo *proxyInfo); michael@0: michael@0: // this method instructs the socket transport to use an already connected michael@0: // socket with the given address. michael@0: nsresult InitWithConnectedSocket(PRFileDesc *socketFD, michael@0: const mozilla::net::NetAddr *addr); michael@0: michael@0: // This method instructs the socket transport to open a socket michael@0: // connected to the given Unix domain address. We can only create michael@0: // unlayered, simple, stream sockets. michael@0: nsresult InitWithFilename(const char *filename); michael@0: michael@0: // nsASocketHandler methods: michael@0: void OnSocketReady(PRFileDesc *, int16_t outFlags); michael@0: void OnSocketDetached(PRFileDesc *); michael@0: void IsLocal(bool *aIsLocal); michael@0: void OnKeepaliveEnabledPrefChange(bool aEnabled) MOZ_OVERRIDE MOZ_FINAL; michael@0: michael@0: // called when a socket event is handled michael@0: void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param); michael@0: michael@0: uint64_t ByteCountReceived() { return mInput.ByteCount(); } michael@0: uint64_t ByteCountSent() { return mOutput.ByteCount(); } michael@0: protected: michael@0: michael@0: virtual ~nsSocketTransport(); michael@0: michael@0: private: michael@0: michael@0: // event types michael@0: enum { michael@0: MSG_ENSURE_CONNECT, michael@0: MSG_DNS_LOOKUP_COMPLETE, michael@0: MSG_RETRY_INIT_SOCKET, michael@0: MSG_TIMEOUT_CHANGED, michael@0: MSG_INPUT_CLOSED, michael@0: MSG_INPUT_PENDING, michael@0: MSG_OUTPUT_CLOSED, michael@0: MSG_OUTPUT_PENDING michael@0: }; michael@0: nsresult PostEvent(uint32_t type, nsresult status = NS_OK, nsISupports *param = nullptr); michael@0: michael@0: enum { michael@0: STATE_CLOSED, michael@0: STATE_IDLE, michael@0: STATE_RESOLVING, michael@0: STATE_CONNECTING, michael@0: STATE_TRANSFERRING, michael@0: STATE_SENDINGGET, michael@0: STATE_SENTGET michael@0: }; michael@0: michael@0: // Safer way to get and automatically release PRFileDesc objects. michael@0: class MOZ_STACK_CLASS PRFileDescAutoLock michael@0: { michael@0: public: michael@0: typedef mozilla::MutexAutoLock MutexAutoLock; michael@0: michael@0: PRFileDescAutoLock(nsSocketTransport *aSocketTransport, michael@0: nsresult *aConditionWhileLocked = nullptr) michael@0: : mSocketTransport(aSocketTransport) michael@0: , mFd(nullptr) michael@0: { michael@0: MOZ_ASSERT(aSocketTransport); michael@0: MutexAutoLock lock(mSocketTransport->mLock); michael@0: if (aConditionWhileLocked) { michael@0: *aConditionWhileLocked = mSocketTransport->mCondition; michael@0: if (NS_FAILED(mSocketTransport->mCondition)) { michael@0: return; michael@0: } michael@0: } michael@0: mFd = mSocketTransport->GetFD_Locked(); michael@0: } michael@0: ~PRFileDescAutoLock() { michael@0: MutexAutoLock lock(mSocketTransport->mLock); michael@0: if (mFd) { michael@0: mSocketTransport->ReleaseFD_Locked(mFd); michael@0: } michael@0: } michael@0: bool IsInitialized() { michael@0: return mFd; michael@0: } michael@0: operator PRFileDesc*() { michael@0: return mFd; michael@0: } michael@0: nsresult SetKeepaliveEnabled(bool aEnable); michael@0: nsresult SetKeepaliveVals(bool aEnabled, int aIdleTime, michael@0: int aRetryInterval, int aProbeCount); michael@0: private: michael@0: operator PRFileDescAutoLock*() { return nullptr; } michael@0: michael@0: // Weak ptr to nsSocketTransport since this is a stack class only. michael@0: nsSocketTransport *mSocketTransport; michael@0: PRFileDesc *mFd; michael@0: }; michael@0: friend class PRFileDescAutoLock; michael@0: michael@0: class LockedPRFileDesc michael@0: { michael@0: public: michael@0: LockedPRFileDesc(nsSocketTransport *aSocketTransport) michael@0: : mSocketTransport(aSocketTransport) michael@0: , mFd(nullptr) michael@0: { michael@0: MOZ_ASSERT(aSocketTransport); michael@0: } michael@0: ~LockedPRFileDesc() {} michael@0: bool IsInitialized() { michael@0: return mFd; michael@0: } michael@0: LockedPRFileDesc& operator=(PRFileDesc *aFd) { michael@0: mSocketTransport->mLock.AssertCurrentThreadOwns(); michael@0: mFd = aFd; michael@0: return *this; michael@0: } michael@0: operator PRFileDesc*() { michael@0: if (mSocketTransport->mAttached) { michael@0: mSocketTransport->mLock.AssertCurrentThreadOwns(); michael@0: } michael@0: return mFd; michael@0: } michael@0: bool operator==(PRFileDesc *aFd) { michael@0: mSocketTransport->mLock.AssertCurrentThreadOwns(); michael@0: return mFd == aFd; michael@0: } michael@0: private: michael@0: operator LockedPRFileDesc*() { return nullptr; } michael@0: // Weak ptr to nsSocketTransport since it owns this class. michael@0: nsSocketTransport *mSocketTransport; michael@0: PRFileDesc *mFd; michael@0: }; michael@0: friend class LockedPRFileDesc; michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // these members are "set" at initialization time and are never modified michael@0: // afterwards. this allows them to be safely accessed from any thread. michael@0: //------------------------------------------------------------------------- michael@0: michael@0: // socket type info: michael@0: char **mTypes; michael@0: uint32_t mTypeCount; michael@0: nsCString mHost; michael@0: uint16_t mPort; michael@0: bool mHttpsProxy; michael@0: michael@0: nsCOMPtr mProxyInfo; michael@0: bool mProxyUse; michael@0: bool mProxyTransparent; michael@0: bool mProxyTransparentResolvesHost; michael@0: uint32_t mConnectionFlags; michael@0: michael@0: uint16_t SocketPort(); michael@0: const nsCString &SocketHost(); michael@0: nsCString mProxyHostCache; // for SocketHost() only michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // members accessible only on the socket transport thread: michael@0: // (the exception being initialization/shutdown time) michael@0: //------------------------------------------------------------------------- michael@0: michael@0: // socket state vars: michael@0: uint32_t mState; // STATE_??? flags michael@0: bool mAttached; michael@0: bool mInputClosed; michael@0: bool mOutputClosed; michael@0: michael@0: // this flag is used to determine if the results of a host lookup arrive michael@0: // recursively or not. this flag is not protected by any lock. michael@0: bool mResolving; michael@0: michael@0: nsCOMPtr mDNSRequest; michael@0: nsCOMPtr mDNSRecord; michael@0: michael@0: // mNetAddr is valid from GetPeerAddr() once we have michael@0: // reached STATE_TRANSFERRING. It must not change after that. michael@0: mozilla::net::NetAddr mNetAddr; michael@0: bool mNetAddrIsSet; michael@0: michael@0: // socket methods (these can only be called on the socket thread): michael@0: michael@0: void SendStatus(nsresult status); michael@0: nsresult ResolveHost(); michael@0: nsresult BuildSocket(PRFileDesc *&, bool &, bool &); michael@0: nsresult InitiateSocket(); michael@0: bool RecoverFromError(); michael@0: michael@0: void OnMsgInputPending() michael@0: { michael@0: if (mState == STATE_TRANSFERRING) michael@0: mPollFlags |= (PR_POLL_READ | PR_POLL_EXCEPT); michael@0: } michael@0: void OnMsgOutputPending() michael@0: { michael@0: if (mState == STATE_TRANSFERRING) michael@0: mPollFlags |= (PR_POLL_WRITE | PR_POLL_EXCEPT); michael@0: } michael@0: void OnMsgInputClosed(nsresult reason); michael@0: void OnMsgOutputClosed(nsresult reason); michael@0: michael@0: // called when the socket is connected michael@0: void OnSocketConnected(); michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // socket input/output objects. these may be accessed on any thread with michael@0: // the exception of some specific methods (XXX). michael@0: michael@0: Mutex mLock; // protects members in this section. michael@0: LockedPRFileDesc mFD; michael@0: nsrefcnt mFDref; // mFD is closed when mFDref goes to zero. michael@0: bool mFDconnected; // mFD is available to consumer when TRUE. michael@0: michael@0: // A delete protector reference to gSocketTransportService held for lifetime michael@0: // of 'this'. Sometimes used interchangably with gSocketTransportService due michael@0: // to scoping. michael@0: nsRefPtr mSocketTransportService; michael@0: michael@0: nsCOMPtr mCallbacks; michael@0: nsCOMPtr mEventSink; michael@0: nsCOMPtr mSecInfo; michael@0: michael@0: nsSocketInputStream mInput; michael@0: nsSocketOutputStream mOutput; michael@0: michael@0: friend class nsSocketInputStream; michael@0: friend class nsSocketOutputStream; michael@0: michael@0: // socket timeouts are not protected by any lock. michael@0: uint16_t mTimeouts[2]; michael@0: michael@0: // QoS setting for socket michael@0: uint8_t mQoSBits; michael@0: michael@0: // michael@0: // mFD access methods: called with mLock held. michael@0: // michael@0: PRFileDesc *GetFD_Locked(); michael@0: void ReleaseFD_Locked(PRFileDesc *fd); michael@0: michael@0: // michael@0: // stream state changes (called outside mLock): michael@0: // michael@0: void OnInputClosed(nsresult reason) michael@0: { michael@0: // no need to post an event if called on the socket thread michael@0: if (PR_GetCurrentThread() == gSocketThread) michael@0: OnMsgInputClosed(reason); michael@0: else michael@0: PostEvent(MSG_INPUT_CLOSED, reason); michael@0: } michael@0: void OnInputPending() michael@0: { michael@0: // no need to post an event if called on the socket thread michael@0: if (PR_GetCurrentThread() == gSocketThread) michael@0: OnMsgInputPending(); michael@0: else michael@0: PostEvent(MSG_INPUT_PENDING); michael@0: } michael@0: void OnOutputClosed(nsresult reason) michael@0: { michael@0: // no need to post an event if called on the socket thread michael@0: if (PR_GetCurrentThread() == gSocketThread) michael@0: OnMsgOutputClosed(reason); // XXX need to not be inside lock! michael@0: else michael@0: PostEvent(MSG_OUTPUT_CLOSED, reason); michael@0: } michael@0: void OnOutputPending() michael@0: { michael@0: // no need to post an event if called on the socket thread michael@0: if (PR_GetCurrentThread() == gSocketThread) michael@0: OnMsgOutputPending(); michael@0: else michael@0: PostEvent(MSG_OUTPUT_PENDING); michael@0: } michael@0: michael@0: #ifdef ENABLE_SOCKET_TRACING michael@0: void TraceInBuf(const char *buf, int32_t n); michael@0: void TraceOutBuf(const char *buf, int32_t n); michael@0: #endif michael@0: michael@0: // Reads prefs to get default keepalive config. michael@0: nsresult EnsureKeepaliveValsAreInitialized(); michael@0: michael@0: // Groups calls to fd.SetKeepaliveEnabled and fd.SetKeepaliveVals. michael@0: nsresult SetKeepaliveEnabledInternal(bool aEnable); michael@0: michael@0: // True if keepalive has been enabled by the socket owner. Note: Keepalive michael@0: // must also be enabled globally for it to be enabled in TCP. michael@0: bool mKeepaliveEnabled; michael@0: michael@0: // Keepalive config (support varies by platform). michael@0: int32_t mKeepaliveIdleTimeS; michael@0: int32_t mKeepaliveRetryIntervalS; michael@0: int32_t mKeepaliveProbeCount; michael@0: }; michael@0: michael@0: #endif // !nsSocketTransport_h__