Bug 1266667: socket reuse option for TCPSocket. r?mcmanus draft
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Wed, 21 Dec 2016 23:38:06 -0800
changeset 454985 d7ff24fcafd4361b43ab1df0c453bdc48e6694ea
parent 454949 e120594f18fb68bf0cc9fe119100210f8b4a354c
child 454986 660ccb10a150159ea615af6ec9c58781b850744a
child 457227 2dc4b672165b707808273bc37ce7dcf95d544f06
push id40099
push userdrno@ohlmeier.org
push dateSat, 31 Dec 2016 00:37:31 +0000
reviewersmcmanus
bugs1266667
milestone53.0a1
Bug 1266667: socket reuse option for TCPSocket. r?mcmanus MozReview-Commit-ID: CJEK714ruLl
dom/network/PTCPSocket.ipdl
dom/network/TCPSocketChild.cpp
dom/network/TCPSocketChild.h
dom/network/TCPSocketParent.cpp
dom/network/TCPSocketParent.h
media/mtransport/nr_socket_prsock.cpp
netwerk/base/nsISocketTransport.idl
netwerk/base/nsSocketTransport2.cpp
netwerk/base/nsSocketTransport2.h
netwerk/protocol/http/TunnelUtils.cpp
--- a/dom/network/PTCPSocket.ipdl
+++ b/dom/network/PTCPSocket.ipdl
@@ -40,17 +40,18 @@ parent:
   // is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
   // |binaryType| (from TCPOption.binaryType).
   async Open(nsString host, uint16_t port, bool useSSL, bool useArrayBuffers);
 
   // Ask parent to open a socket and bind the newly-opened socket to a local
   // address specified in |localAddr| and |localPort|.
   async OpenBind(nsCString host, uint16_t port,
                  nsCString localAddr, uint16_t localPort,
-                 bool useSSL, bool aUseArrayBuffers, nsCString aFilter);
+                 bool useSSL, bool reuseAddrPort,
+                 bool aUseArrayBuffers, nsCString aFilter);
 
   // When child's send() is called, this message requrests parent to send
   // data and update it's trackingNumber.
   async Data(SendableData data, uint32_t trackingNumber);
 
   // Forward calling to child's upgradeToSecure() method to parent.
   async StartTLS();
 
--- a/dom/network/TCPSocketChild.cpp
+++ b/dom/network/TCPSocketChild.cpp
@@ -105,26 +105,27 @@ TCPSocketChild::SendOpen(nsITCPSocketCal
   MOZ_ASSERT(mFilterName.IsEmpty()); // Currently nobody should use this
   PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
 }
 
 void
 TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
                                        const nsACString& aRemoteHost, uint16_t aRemotePort,
                                        const nsACString& aLocalHost, uint16_t aLocalPort,
-                                       bool aUseSSL)
+                                       bool aUseSSL, bool aReuseAddrPort)
 {
   mSocket = aSocket;
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this,
                                          NS_ConvertUTF8toUTF16(aRemoteHost),
                                          aRemotePort);
   PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort,
                                 nsCString(aLocalHost), aLocalPort,
-                                aUseSSL, true, mFilterName);
+                                aUseSSL, aReuseAddrPort,
+                                true, mFilterName);
 }
 
 void
 TCPSocketChildBase::ReleaseIPDLReference()
 {
   MOZ_ASSERT(mIPCOpen);
   mIPCOpen = false;
   mSocket = nullptr;
--- a/dom/network/TCPSocketChild.h
+++ b/dom/network/TCPSocketChild.h
@@ -51,17 +51,17 @@ public:
 
   TCPSocketChild(const nsAString& aHost, const uint16_t& aPort);
   ~TCPSocketChild();
 
   void SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers);
   void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
                               const nsACString& aRemoteHost, uint16_t aRemotePort,
                               const nsACString& aLocalHost, uint16_t aLocalPort,
-                              bool aUseSSL);
+                              bool aUseSSL, bool aUseRealtimeOptions);
   NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray,
                            uint32_t aTrackingNumber);
   void SendSend(const nsACString& aData, uint32_t aTrackingNumber);
   nsresult SendSend(const ArrayBuffer& aData,
                     uint32_t aByteOffset,
                     uint32_t aByteLength,
                     uint32_t aTrackingNumber);
   void SendSendArray(nsTArray<uint8_t>* arr,
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -129,16 +129,17 @@ TCPSocketParent::RecvOpen(const nsString
 }
 
 mozilla::ipc::IPCResult
 TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
                               const uint16_t& aRemotePort,
                               const nsCString& aLocalAddr,
                               const uint16_t& aLocalPort,
                               const bool&     aUseSSL,
+                              const bool&     aReuseAddrPort,
                               const bool&     aUseArrayBuffers,
                               const nsCString& aFilter)
 {
   nsresult rv;
   nsCOMPtr<nsISocketTransportService> sts =
     do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
   if (NS_FAILED(rv)) {
     FireInteralError(this, __LINE__);
@@ -149,16 +150,20 @@ TCPSocketParent::RecvOpenBind(const nsCS
   rv = sts->CreateTransport(nullptr, 0,
                             aRemoteHost, aRemotePort,
                             nullptr, getter_AddRefs(socketTransport));
   if (NS_FAILED(rv)) {
     FireInteralError(this, __LINE__);
     return IPC_OK();
   }
 
+  // in most cases aReuseAddrPort is false, but ICE TCP needs
+  // sockets options set that allow addr/port reuse
+  socketTransport->SetReuseAddrPort(aReuseAddrPort);
+
   PRNetAddr prAddr;
   if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
     FireInteralError(this, __LINE__);
     return IPC_OK();
   }
   if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
     FireInteralError(this, __LINE__);
     return IPC_OK();
--- a/dom/network/TCPSocketParent.h
+++ b/dom/network/TCPSocketParent.h
@@ -50,16 +50,17 @@ public:
   virtual mozilla::ipc::IPCResult RecvOpen(const nsString& aHost, const uint16_t& aPort,
                                            const bool& useSSL, const bool& aUseArrayBuffers) override;
 
   virtual mozilla::ipc::IPCResult RecvOpenBind(const nsCString& aRemoteHost,
                                                const uint16_t& aRemotePort,
                                                const nsCString& aLocalAddr,
                                                const uint16_t& aLocalPort,
                                                const bool&     aUseSSL,
+                                               const bool&     aReuseAddrPort,
                                                const bool& aUseArrayBuffers,
                                                const nsCString& aFilter) override;
 
   virtual mozilla::ipc::IPCResult RecvStartTLS() override;
   virtual mozilla::ipc::IPCResult RecvSuspend() override;
   virtual mozilla::ipc::IPCResult RecvResume() override;
   virtual mozilla::ipc::IPCResult RecvClose() override;
   virtual mozilla::ipc::IPCResult RecvData(const SendableData& aData,
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -1963,17 +1963,18 @@ void NrTcpSocketIpc::connect_i(const nsA
   socket_child_ = child;
 
   // Bug 1285330: put filtering back in here
 
   // XXX remove remote!
   socket_child_->SendWindowlessOpenBind(this,
                                         remote_addr, remote_port,
                                         local_addr, local_port,
-                                        /* use ssl */ false);
+                                        /* use ssl */ false,
+                                        /* reuse addr port */ true);
 }
 
 void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
                              uint32_t tracking_number) {
   ASSERT_ON_THREAD(io_thread_);
   if (!socket_child_) {
     return;
   }
--- a/netwerk/base/nsISocketTransport.idl
+++ b/netwerk/base/nsISocketTransport.idl
@@ -125,16 +125,21 @@ interface nsISocketTransport : nsITransp
      * Socket timeouts in seconds.  To specify no timeout, pass UINT32_MAX
      * as aValue to setTimeout.  The implementation may truncate timeout values
      * to a smaller range of values (e.g., 0 to 0xFFFF).
      */
     unsigned long getTimeout(in unsigned long aType);
     void          setTimeout(in unsigned long aType, in unsigned long aValue);
 
     /**
+     * True to set addr and port reuse socket options.
+     */
+    void setReuseAddrPort(in bool reuseAddrPort);
+
+    /**
      * Values for the aType parameter passed to get/setTimeout.
      */
     const unsigned long TIMEOUT_CONNECT    = 0;
     const unsigned long TIMEOUT_READ_WRITE = 1;
 
     /**
      * nsITransportEventSink status codes.
      *
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -735,16 +735,17 @@ nsSocketTransport::nsSocketTransport()
     , mTypeCount(0)
     , mPort(0)
     , mProxyPort(0)
     , mOriginPort(0)
     , mProxyTransparent(false)
     , mProxyTransparentResolvesHost(false)
     , mHttpsProxy(false)
     , mConnectionFlags(0)
+    , mReuseAddrPort(false)
     , mState(STATE_CLOSED)
     , mAttached(false)
     , mInputClosed(true)
     , mOutputClosed(true)
     , mResolving(false)
     , mNetAddrIsSet(false)
     , mSelfAddrIsSet(false)
     , mNetAddrPreResolved(false)
@@ -1349,16 +1350,42 @@ nsSocketTransport::InitiateSocket()
 
     // Make the socket non-blocking...
     PRSocketOptionData opt;
     opt.option = PR_SockOpt_Nonblocking;
     opt.value.non_blocking = true;
     status = PR_SetSocketOption(fd, &opt);
     NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
 
+    if (mReuseAddrPort) {
+        SOCKET_LOG(("  Setting port/addr reuse socket options\n"));
+
+        // Set ReuseAddr for TCP sockets to enable having several
+        // sockets bound to same local IP and port
+        PRSocketOptionData opt_reuseaddr;
+        opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
+        opt_reuseaddr.value.reuse_addr = PR_TRUE;
+        status = PR_SetSocketOption(fd, &opt_reuseaddr);
+        if (status != PR_SUCCESS) {
+            SOCKET_LOG(("  Couldn't set reuse addr socket option: %d\n",
+                        status));
+        }
+
+        // And also set ReusePort for platforms supporting this socket option
+        PRSocketOptionData opt_reuseport;
+        opt_reuseport.option = PR_SockOpt_Reuseport;
+        opt_reuseport.value.reuse_port = PR_TRUE;
+        status = PR_SetSocketOption(fd, &opt_reuseport);
+        if (status != PR_SUCCESS
+            && PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
+            SOCKET_LOG(("  Couldn't set reuse port socket option: %d\n",
+                        status));
+        }
+    }
+
     // disable the nagle algorithm - if we rely on it to coalesce writes into
     // full packets the final packet of a multi segment POST/PUT or pipeline
     // sequence is delayed a full rtt
     opt.option = PR_SockOpt_NoDelay;
     opt.value.no_delay = true;
     PR_SetSocketOption(fd, &opt);
 
     // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
@@ -2534,16 +2561,23 @@ nsSocketTransport::SetTimeout(uint32_t t
     NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
     // truncate overly large timeout values.
     mTimeouts[type] = (uint16_t) std::min<uint32_t>(value, UINT16_MAX);
     PostEvent(MSG_TIMEOUT_CHANGED);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort)
+{
+  mReuseAddrPort = reuseAddrPort;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSocketTransport::SetQoSBits(uint8_t aQoSBits)
 {
     // Don't do any checking here of bits.  Why?  Because as of RFC-4594
     // several different Class Selector and Assured Forwarding values
     // have been defined, but that isn't to say more won't be added later.
     // In that case, any checking would be an impediment to interoperating
     // with newer QoS definitions.
 
--- a/netwerk/base/nsSocketTransport2.h
+++ b/netwerk/base/nsSocketTransport2.h
@@ -297,16 +297,17 @@ private:
     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.
     NeckoOriginAttributes mOriginAttributes;
     
--- a/netwerk/protocol/http/TunnelUtils.cpp
+++ b/netwerk/protocol/http/TunnelUtils.cpp
@@ -1647,16 +1647,22 @@ SocketTransportShim::SetNetworkInterface
 
 NS_IMETHODIMP
 SocketTransportShim::SetTimeout(uint32_t aType, uint32_t aValue)
 {
   return mWrapped->SetTimeout(aType, aValue);
 }
 
 NS_IMETHODIMP
+SocketTransportShim::SetReuseAddrPort(bool aReuseAddrPort)
+{
+  return mWrapped->SetReuseAddrPort(aReuseAddrPort);
+}
+
+NS_IMETHODIMP
 SocketTransportShim::GetQoSBits(uint8_t *aQoSBits)
 {
   return mWrapped->GetQoSBits(aQoSBits);
 }
 
 NS_IMETHODIMP
 SocketTransportShim::SetQoSBits(uint8_t aQoSBits)
 {