author Dragana Damjanovic <>
Thu, 27 Jul 2017 11:37:11 +0200
changeset 373956 d77b4d6c322d64e66033f3731fbb2103cd1405cf
parent 360320 c73ef4420951a1fad4b9d91d2216c3e3d3f31baf
child 375100 bfe9b1d9ad1193b3d4b4e026d7dfbf998603f3db
permissions -rw-r--r--
Bug 1363372 - Wait until OVERLAPPED structure return a result before distroying a socket. r=mcmanus

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at */

#ifndef nsSocketTransport2_h__
#define nsSocketTransport2_h__

#ifdef DEBUG_darinf

#include "mozilla/Mutex.h"
#include "nsSocketTransportService2.h"
#include "nsString.h"
#include "nsCOMPtr.h"

#include "nsIInterfaceRequestor.h"
#include "nsISocketTransport.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsIDNSListener.h"
#include "nsIClassInfo.h"
#include "TCPFastOpen.h"
#include "mozilla/net/DNS.h"
#include "nsASocketHandler.h"
#include "mozilla/Telemetry.h"

#include "prerror.h"
#include "nsAutoPtr.h"

class nsICancelable;
class nsIDNSRecord;
class nsIInterfaceRequestor;


// after this short interval, we will return to PR_Poll
#define NS_SOCKET_CONNECT_TIMEOUT PR_MillisecondsToInterval(20)


namespace mozilla {
namespace net {

ErrorAccordingToNSPR(PRErrorCode errorCode);

class nsSocketTransport;

class nsSocketInputStream : public nsIAsyncInputStream

    explicit nsSocketInputStream(nsSocketTransport *);
    virtual ~nsSocketInputStream();

    bool     IsReferenced() { return mReaderRefCnt > 0; }
    nsresult Condition()    { return mCondition; }
    uint64_t ByteCount()    { return mByteCount; }

    // called by the socket transport on the socket thread...
    void OnSocketReady(nsresult condition);

    nsSocketTransport               *mTransport;
    ThreadSafeAutoRefCnt             mReaderRefCnt;

    // access to these is protected by mTransport->mLock
    nsresult                         mCondition;
    nsCOMPtr<nsIInputStreamCallback> mCallback;
    uint32_t                         mCallbackFlags;
    uint64_t                         mByteCount;


class nsSocketOutputStream : public nsIAsyncOutputStream

    explicit nsSocketOutputStream(nsSocketTransport *);
    virtual ~nsSocketOutputStream();

    bool     IsReferenced() { return mWriterRefCnt > 0; }
    nsresult Condition()    { return mCondition; }
    uint64_t ByteCount()    { return mByteCount; }

    // called by the socket transport on the socket thread...
    void OnSocketReady(nsresult condition);

    static nsresult WriteFromSegments(nsIInputStream *, void *,
                                      const char *, uint32_t offset,
                                      uint32_t count, uint32_t *countRead);

    nsSocketTransport                *mTransport;
    ThreadSafeAutoRefCnt              mWriterRefCnt;

    // access to these is protected by mTransport->mLock
    nsresult                          mCondition;
    nsCOMPtr<nsIOutputStreamCallback> mCallback;
    uint32_t                          mCallbackFlags;
    uint64_t                          mByteCount;


class nsSocketTransport final : public nsASocketHandler
                              , public nsISocketTransport
                              , public nsIDNSListener
                              , public nsIClassInfo
                              , public nsIInterfaceRequestor


    // this method instructs the socket transport to open a socket of the
    // given type(s) to the given host or proxy.
    nsresult Init(const char **socketTypes, uint32_t typeCount,
                  const nsACString &host, uint16_t port,
                  const nsACString &hostRoute, uint16_t portRoute,
                  nsIProxyInfo *proxyInfo);

    // Alternative Init method for when the IP-address of the host
    // has been pre-resolved using a alternative means (e.g. FlyWeb service
    // info).
    nsresult InitPreResolved(const char **socketTypes, uint32_t typeCount,
                             const nsACString &host, uint16_t port,
                             const nsACString &hostRoute, uint16_t portRoute,
                             nsIProxyInfo *proxyInfo,
                             const mozilla::net::NetAddr* addr);

    // this method instructs the socket transport to use an already connected
    // socket with the given address.
    nsresult InitWithConnectedSocket(PRFileDesc *socketFD,
                                     const NetAddr *addr);

    // this method instructs the socket transport to use an already connected
    // socket with the given address, and additionally supplies security info.
    nsresult InitWithConnectedSocket(PRFileDesc* aSocketFD,
                                     const NetAddr* aAddr,
                                     nsISupports* aSecInfo);

    // This method instructs the socket transport to open a socket
    // connected to the given Unix domain address. We can only create
    // unlayered, simple, stream sockets.
    nsresult InitWithFilename(const char *filename);

    // nsASocketHandler methods:
    void OnSocketReady(PRFileDesc *, int16_t outFlags) override;
    void OnSocketDetached(PRFileDesc *) override;
    void IsLocal(bool *aIsLocal) override;
    void OnKeepaliveEnabledPrefChange(bool aEnabled) override final;

    // called when a socket event is handled
    void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param);

    uint64_t ByteCountReceived() override { return mInput.ByteCount(); }
    uint64_t ByteCountSent() override { return mOutput.ByteCount(); }
    static void CloseSocket(PRFileDesc *aFd, nsSocketTransportService *aSTS);
    static void SendPRBlockingTelemetry(PRIntervalTime aStart,
        Telemetry::HistogramID aIDNormal,
        Telemetry::HistogramID aIDShutdown,
        Telemetry::HistogramID aIDConnectivityChange,
        Telemetry::HistogramID aIDLinkChange,
        Telemetry::HistogramID aIDOffline);

    virtual ~nsSocketTransport();
    void     CleanupTypes();


    // event types
    enum {
    nsresult PostEvent(uint32_t type, nsresult status = NS_OK, nsISupports *param = nullptr);

    enum {

    // Safer way to get and automatically release PRFileDesc objects.
    class MOZ_STACK_CLASS PRFileDescAutoLock
      explicit PRFileDescAutoLock(nsSocketTransport *aSocketTransport,
                                  bool aAlsoDuringFastOpen,
                                  nsresult *aConditionWhileLocked = nullptr)
        : mSocketTransport(aSocketTransport)
        , mFd(nullptr)
        MutexAutoLock lock(mSocketTransport->mLock);
        if (aConditionWhileLocked) {
          *aConditionWhileLocked = mSocketTransport->mCondition;
          if (NS_FAILED(mSocketTransport->mCondition)) {
        if (!aAlsoDuringFastOpen) {
          mFd = mSocketTransport->GetFD_Locked();
        } else {
          mFd = mSocketTransport->GetFD_LockedAlsoDuringFastOpen();
      ~PRFileDescAutoLock() {
        MutexAutoLock lock(mSocketTransport->mLock);
        if (mFd) {
      bool IsInitialized() {
        return mFd;
      operator PRFileDesc*() {
        return mFd;
      nsresult SetKeepaliveEnabled(bool aEnable);
      nsresult SetKeepaliveVals(bool aEnabled, int aIdleTime,
                                int aRetryInterval, int aProbeCount);
      operator PRFileDescAutoLock*() { return nullptr; }

      // Weak ptr to nsSocketTransport since this is a stack class only.
      nsSocketTransport *mSocketTransport;
      PRFileDesc        *mFd;
    friend class PRFileDescAutoLock;

    class LockedPRFileDesc
      explicit LockedPRFileDesc(nsSocketTransport *aSocketTransport)
        : mSocketTransport(aSocketTransport)
        , mFd(nullptr)
      ~LockedPRFileDesc() {}
      bool IsInitialized() {
        return mFd;
      LockedPRFileDesc& operator=(PRFileDesc *aFd) {
        mFd = aFd;
        return *this;
      operator PRFileDesc*() {
        if (mSocketTransport->mAttached) {
        return mFd;
      bool operator==(PRFileDesc *aFd) {
        return mFd == aFd;
      operator LockedPRFileDesc*() { return nullptr; }
      // Weak ptr to nsSocketTransport since it owns this class.
      nsSocketTransport *mSocketTransport;
      PRFileDesc        *mFd;
    friend class LockedPRFileDesc;

    // these members are "set" at initialization time and are never modified
    // afterwards.  this allows them to be safely accessed from any thread.

    // socket type info:
    char       **mTypes;
    uint32_t     mTypeCount;
    nsCString    mHost;
    nsCString    mProxyHost;
    nsCString    mOriginHost;
    uint16_t     mPort;
    nsCOMPtr<nsIProxyInfo> mProxyInfo;
    uint16_t     mProxyPort;
    uint16_t     mOriginPort;
    bool mProxyTransparent;
    bool mProxyTransparentResolvesHost;
    bool mHttpsProxy;
    uint32_t     mConnectionFlags;
    bool mReuseAddrPort;

    // The origin attributes are used to create sockets.  The first party domain
    // will eventually be used to isolate OCSP cache and is only non-empty when
    // "privacy.firstparty.isolate" is enabled.  Setting this is the only way to
    // carry origin attributes down to NSPR layers which are final consumers.
    // It must be set before the socket transport is built.
    OriginAttributes mOriginAttributes;

    uint16_t         SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }
    const nsCString &SocketHost() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; }

    // members accessible only on the socket transport thread:
    //  (the exception being initialization/shutdown time)

    // socket state vars:
    uint32_t     mState;     // STATE_??? flags
    bool mAttached;
    bool mInputClosed;
    bool mOutputClosed;

    // The platform-specific network interface id that this socket
    // associated with.
    nsCString mNetworkInterfaceId;

    // this flag is used to determine if the results of a host lookup arrive
    // recursively or not.  this flag is not protected by any lock.
    bool mResolving;

    nsCOMPtr<nsICancelable> mDNSRequest;
    nsCOMPtr<nsIDNSRecord>  mDNSRecord;

    // mNetAddr/mSelfAddr is valid from GetPeerAddr()/GetSelfAddr() once we have
    // reached STATE_TRANSFERRING. It must not change after that.
    void                    SetSocketName(PRFileDesc *fd);
    NetAddr                 mNetAddr;
    NetAddr                 mSelfAddr; // getsockname()
    Atomic<bool, Relaxed>   mNetAddrIsSet;
    Atomic<bool, Relaxed>   mSelfAddrIsSet;
    Atomic<bool, Relaxed>   mNetAddrPreResolved;

    nsAutoPtr<NetAddr>      mBindAddr;

    // socket methods (these can only be called on the socket thread):

    void     SendStatus(nsresult status);
    nsresult ResolveHost();
    nsresult BuildSocket(PRFileDesc *&, bool &, bool &);
    nsresult InitiateSocket();
    bool     RecoverFromError();

    void OnMsgInputPending()
        if (mState == STATE_TRANSFERRING)
            mPollFlags |= (PR_POLL_READ | PR_POLL_EXCEPT);
    void OnMsgOutputPending()
        if (mState == STATE_TRANSFERRING)
            mPollFlags |= (PR_POLL_WRITE | PR_POLL_EXCEPT);
    void OnMsgInputClosed(nsresult reason);
    void OnMsgOutputClosed(nsresult reason);

    // called when the socket is connected
    void OnSocketConnected();

    // socket input/output objects.  these may be accessed on any thread with
    // the exception of some specific methods (XXX).

    Mutex            mLock;  // protects members in this section.
    LockedPRFileDesc mFD;
    nsrefcnt         mFDref;       // mFD is closed when mFDref goes to zero.
    bool             mFDconnected; // mFD is available to consumer when TRUE.
    bool             mFDFastOpenInProgress; // Fast Open is in progress, so
                                            // socket available for some
                                            // operations.

    // A delete protector reference to gSocketTransportService held for lifetime
    // of 'this'. Sometimes used interchangably with gSocketTransportService due
    // to scoping.
    RefPtr<nsSocketTransportService> mSocketTransportService;

    nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
    nsCOMPtr<nsITransportEventSink> mEventSink;
    nsCOMPtr<nsISupports>           mSecInfo;

    nsSocketInputStream  mInput;
    nsSocketOutputStream mOutput;

    friend class nsSocketInputStream;
    friend class nsSocketOutputStream;

    // socket timeouts are not protected by any lock.
    uint16_t mTimeouts[2];

    // QoS setting for socket
    uint8_t mQoSBits;

    // mFD access methods: called with mLock held.
    PRFileDesc *GetFD_Locked();
    PRFileDesc *GetFD_LockedAlsoDuringFastOpen();
    void        ReleaseFD_Locked(PRFileDesc *fd);
    bool FastOpenInProgress();

    // stream state changes (called outside mLock):
    void OnInputClosed(nsresult reason)
        // no need to post an event if called on the socket thread
        if (OnSocketThread())
            PostEvent(MSG_INPUT_CLOSED, reason);
    void OnInputPending()
        // no need to post an event if called on the socket thread
        if (OnSocketThread())
    void OnOutputClosed(nsresult reason)
        // no need to post an event if called on the socket thread
        if (OnSocketThread())
            OnMsgOutputClosed(reason); // XXX need to not be inside lock!
            PostEvent(MSG_OUTPUT_CLOSED, reason);
    void OnOutputPending()
        // no need to post an event if called on the socket thread
        if (OnSocketThread())

    void TraceInBuf(const char *buf, int32_t n);
    void TraceOutBuf(const char *buf, int32_t n);

    // Reads prefs to get default keepalive config.
    nsresult EnsureKeepaliveValsAreInitialized();

    // Groups calls to fd.SetKeepaliveEnabled and fd.SetKeepaliveVals.
    nsresult SetKeepaliveEnabledInternal(bool aEnable);

    // True if keepalive has been enabled by the socket owner. Note: Keepalive
    // must also be enabled globally for it to be enabled in TCP.
    bool mKeepaliveEnabled;

    // Keepalive config (support varies by platform).
    int32_t mKeepaliveIdleTimeS;
    int32_t mKeepaliveRetryIntervalS;
    int32_t mKeepaliveProbeCount;

    // A Fast Open callback.
    TCPFastOpen *mFastOpenCallback;
    bool mFastOpenLayerHasBufferedData;

    bool mDoNotRetryToConnect;

} // namespace net
} // namespace mozilla

#endif // !nsSocketTransport_h__