Bug 745283 - Part 1: Expose nsINetAddr for the local address of nsUDPSocket and support send with InputStream. r=jduell, r=mayhemer
☠☠ backed out by 607421044238 ☠ ☠
authorShih-Chiang Chien <schien@mozilla.com>
Mon, 21 Apr 2014 09:33:00 +0800
changeset 223273 bc0ab47e8ac775d49d6e8a57d8e99be112918d39
parent 223272 b8a962a66a800854213c81bdeb180a351ac54004
child 223274 37c10c9f2a4db72f1c7ba00aa714a43900d5a638
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell, mayhemer
bugs745283
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 745283 - Part 1: Expose nsINetAddr for the local address of nsUDPSocket and support send with InputStream. r=jduell, r=mayhemer
netwerk/base/public/nsIUDPSocket.idl
netwerk/base/src/nsUDPSocket.cpp
netwerk/test/TestUDPSocket.cpp
netwerk/test/unit/test_bug952927.js
netwerk/test/unit/test_udpsocket.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/base/public/nsIUDPSocket.idl
+++ b/netwerk/base/public/nsIUDPSocket.idl
@@ -5,16 +5,17 @@
 
 #include "nsISupports.idl"
 
 interface nsINetAddr;
 interface nsIUDPSocketListener;
 interface nsIUDPMessage;
 interface nsISocketTransport;
 interface nsIOutputStream;
+interface nsIInputStream;
 
 %{ C++
 #include "nsTArrayForwardDeclare.h"
 namespace mozilla {
 namespace net {
 union NetAddr;
 }
 }
@@ -23,45 +24,51 @@ native NetAddr(mozilla::net::NetAddr);
 [ptr] native NetAddrPtr(mozilla::net::NetAddr);
 [ref] native Uint8TArrayRef(FallibleTArray<uint8_t>);
 
 /**
  * nsIUDPSocket
  *
  * An interface to a UDP socket that can accept incoming connections.
  */
-[scriptable, uuid(3240F793-80FA-4088-987A-9C7378F0AC88)]
+[scriptable, uuid(5e526cc7-a65f-42b2-b193-a6894c0253f7)]
 interface nsIUDPSocket : nsISupports
 {
     /**
      * init
      *
      * This method initializes a UDP socket.
      *
      * @param aPort
      *        The port of the UDP socket.  Pass -1 to indicate no preference,
      *        and a port will be selected automatically.
      * @param aLoopbackOnly
      *        If true, the UDP socket will only respond to connections on the
      *        local loopback interface.  Otherwise, it will accept connections
      *        from any interface.  To specify a particular network interface,
      *        use initWithAddress.
+     * @param aAddressReuse
+     *        If true, the socket is allowed to be bound to an address that is
+     *        already in use. Default is true.
      */
-    void init(in long aPort, in boolean aLoopbackOnly);
+    [optional_argc] void init(in long aPort, in boolean aLoopbackOnly, [optional] in boolean aAddressReuse);
 
     /**
      * initWithAddress
      *
      * This method initializes a UDP socket, and binds it to a particular
      * local address (and hence a particular local network interface).
      *
      * @param aAddr
      *        The address to which this UDP socket should be bound.
+     * @param aAddressReuse
+     *        If true, the socket is allowed to be bound to an address that is
+     *        already in use. Default is true.
      */
-    [noscript] void initWithAddress([const] in NetAddrPtr aAddr);
+    [noscript, optional_argc] void initWithAddress([const] in NetAddrPtr aAddr, [optional] in boolean aAddressReuse);
 
     /**
      * close
      *
      * This method closes a UDP socket.  This does not affect already
      * connected client sockets (i.e., the nsISocketTransport instances
      * created from this UDP socket).  This will cause the onStopListening
      * event to asynchronously fire with a status of NS_BINDING_ABORTED.
@@ -81,16 +88,21 @@ interface nsIUDPSocket : nsISupports
      * transport (nsISocketTransport).  See below for more details.
      *
      * @param aListener
      *        The listener to be notified when client connections are accepted.
      */
     void asyncListen(in nsIUDPSocketListener aListener);
 
     /**
+     * Returns the local address of this UDP socket
+     */
+    readonly attribute nsINetAddr localAddr;
+
+    /**
      * Returns the port of this UDP socket.
      */
     readonly attribute long port;
 
     /**
      * Returns the address to which this UDP socket is bound.  Since a
      * UDP socket may be bound to multiple network devices, this address
      * may not necessarily be specific to a single network device.  In the
@@ -141,16 +153,39 @@ interface nsIUDPSocket : nsISupports
      * @param dataLength The maximum number of bytes to be written.
      * @return number of bytes written. (0 or dataLength)
      */
     [noscript] unsigned long sendWithAddress([const] in NetAddrPtr addr,
                                              [const, array, size_is(dataLength)]in uint8_t data,
                                              in unsigned long dataLength);
 
     /**
+     * sendBinaryStream
+     *
+     * Send out the datagram to specified remote address and port.
+     *
+     * @param host The remote host name.
+     * @param port The remote port.
+     * @param stream The input stream to be sent. This must be a buffered stream implementation.
+     */
+    void sendBinaryStream(in AUTF8String host, in unsigned short port,
+                          in nsIInputStream stream);
+
+    /**
+     * sendBinaryStreamWithAddress
+     *
+     * Send out the datagram to specified remote address and port.
+     *
+     * @param addr The remote host address.
+     * @param stream The input stream to be sent. This must be a buffered stream implementation.
+     */
+    void sendBinaryStreamWithAddress([const] in NetAddrPtr addr,
+                                     in nsIInputStream stream);
+
+    /**
      * joinMulticast
      *
      * Join the multicast group specified by |addr|.  You are then able to
      * receive future datagrams addressed to the group.
      *
      * @param addr
      *        The multicast group address.
      * @param iface
--- a/netwerk/base/src/nsUDPSocket.cpp
+++ b/netwerk/base/src/nsUDPSocket.cpp
@@ -26,16 +26,17 @@
 #include "nsIDNSRecord.h"
 #include "nsIDNSService.h"
 #include "nsICancelable.h"
 
 using namespace mozilla::net;
 using namespace mozilla;
 
 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
+static const uint32_t UDP_PACKET_CHUNK_SIZE = 1400;
 
 //-----------------------------------------------------------------------------
 
 typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void);
 
 static nsresult
 PostEvent(nsUDPSocket *s, nsUDPSocketFunc func)
 {
@@ -474,29 +475,29 @@ nsUDPSocket::OnSocketReady(PRFileDesc *f
   if(!data.AppendElements(buff, count)){
     mCondition = NS_ERROR_UNEXPECTED;
     return;
   }
 
   nsCOMPtr<nsIAsyncInputStream> pipeIn;
   nsCOMPtr<nsIAsyncOutputStream> pipeOut;
 
-  uint32_t segsize = 1400;
+  uint32_t segsize = UDP_PACKET_CHUNK_SIZE;
   uint32_t segcount = 0;
   net_ResolveSegmentParams(segsize, segcount);
   nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
                 true, true, segsize, segcount);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
   nsRefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
   rv = NS_AsyncCopy(pipeIn, os, mSts,
-                    NS_ASYNCCOPY_VIA_READSEGMENTS, 1400);
+                    NS_ASYNCCOPY_VIA_READSEGMENTS, UDP_PACKET_CHUNK_SIZE);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
   NetAddr netAddr;
   PRNetAddrToNetAddr(&prClientAddr, &netAddr);
   nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data);
@@ -547,39 +548,41 @@ nsUDPSocket::IsLocal(bool *aIsLocal)
 NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket)
 
 
 //-----------------------------------------------------------------------------
 // nsSocket::nsISocket
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly)
+nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly, bool aAddressReuse, uint8_t aOptionalArgc)
 {
   NetAddr addr;
 
   if (aPort < 0)
     aPort = 0;
 
   addr.raw.family = AF_INET;
   addr.inet.port = htons(aPort);
 
   if (aLoopbackOnly)
     addr.inet.ip = htonl(INADDR_LOOPBACK);
   else
     addr.inet.ip = htonl(INADDR_ANY);
 
-  return InitWithAddress(&addr);
+  return InitWithAddress(&addr, aAddressReuse, aOptionalArgc);
 }
 
 NS_IMETHODIMP
-nsUDPSocket::InitWithAddress(const NetAddr *aAddr)
+nsUDPSocket::InitWithAddress(const NetAddr *aAddr, bool aAddressReuse, uint8_t aOptionalArgc)
 {
   NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
 
+  bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true;
+
   //
   // configure listening socket...
   //
 
   mFD = PR_OpenUDPSocket(aAddr->raw.family);
   if (!mFD)
   {
     NS_WARNING("unable to create UDP socket");
@@ -593,17 +596,17 @@ nsUDPSocket::InitWithAddress(const NetAd
   }
 
   PRSocketOptionData opt;
 
   // Linux kernel will sometimes hand out a used port if we bind
   // to port 0 with SO_REUSEADDR
   if (port) {
     opt.option = PR_SockOpt_Reuseaddr;
-    opt.value.reuse_addr = true;
+    opt.value.reuse_addr = addressReuse;
     PR_SetSocketOption(mFD, &opt);
   }
 
   opt.option = PR_SockOpt_Nonblocking;
   opt.value.non_blocking = true;
   PR_SetSocketOption(mFD, &opt);
 
   PRNetAddr addr;
@@ -664,16 +667,27 @@ nsUDPSocket::GetPort(int32_t *aResult)
   // no need to enter the lock here
   uint16_t result;
   nsresult rv = net::GetPort(&mAddr, &result);
   *aResult = static_cast<int32_t>(result);
   return rv;
 }
 
 NS_IMETHODIMP
+nsUDPSocket::GetLocalAddr(nsINetAddr * *aResult)
+{
+  NS_ENSURE_ARG_POINTER(aResult);
+
+  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
+  result.forget(aResult);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsUDPSocket::GetAddress(NetAddr *aResult)
 {
   // no need to enter the lock here
   memcpy(aResult, &mAddr, sizeof(mAddr));
   return NS_OK;
 }
 
 namespace {
@@ -824,16 +838,57 @@ PendingSend::OnLookupComplete(nsICancela
     nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(),
                                            mData.Length(), &count);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
+class PendingSendStream : public nsIDNSListener
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIDNSLISTENER
+
+  PendingSendStream(nsUDPSocket *aSocket, uint16_t aPort,
+                    nsIInputStream *aStream)
+      : mSocket(aSocket)
+      , mPort(aPort)
+      , mStream(aStream) {}
+
+private:
+  virtual ~PendingSendStream() {}
+
+  nsRefPtr<nsUDPSocket> mSocket;
+  uint16_t mPort;
+  nsCOMPtr<nsIInputStream> mStream;
+};
+
+NS_IMPL_ISUPPORTS(PendingSendStream, nsIDNSListener)
+
+NS_IMETHODIMP
+PendingSendStream::OnLookupComplete(nsICancelable *request,
+                                    nsIDNSRecord  *rec,
+                                    nsresult       status)
+{
+  if (NS_FAILED(status)) {
+    NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
+    return NS_OK;
+  }
+
+  NetAddr addr;
+  if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
+    nsresult rv = mSocket->SendBinaryStreamWithAddress(&addr, mStream);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
 class SendRequestRunnable: public nsRunnable {
 public:
   SendRequestRunnable(nsUDPSocket *aSocket,
                       const NetAddr &aAddr,
                       FallibleTArray<uint8_t> &aData)
     : mSocket(aSocket)
     , mAddr(aAddr)
     , mData(aData)
@@ -948,16 +1003,42 @@ nsUDPSocket::SendWithAddress(const NetAd
     nsresult rv = mSts->Dispatch(new SendRequestRunnable(this, *aAddr, fallibleArray),
                                  NS_DISPATCH_NORMAL);
     NS_ENSURE_SUCCESS(rv, rv);
     *_retval = aDataLength;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsUDPSocket::SendBinaryStream(const nsACString &aHost, uint16_t aPort,
+                              nsIInputStream *aStream)
+{
+  NS_ENSURE_ARG(aStream);
+
+  nsCOMPtr<nsIDNSListener> listener = new PendingSendStream(this, aPort, aStream);
+
+  return ResolveHost(aHost, listener);
+}
+
+NS_IMETHODIMP
+nsUDPSocket::SendBinaryStreamWithAddress(const NetAddr *aAddr, nsIInputStream *aStream)
+{
+  NS_ENSURE_ARG(aAddr);
+  NS_ENSURE_ARG(aStream);
+
+  PRNetAddr prAddr;
+  PR_InitializeNetAddr(PR_IpAddrAny, 0, &prAddr);
+  NetAddrToPRNetAddr(aAddr, &prAddr);
+
+  nsRefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prAddr);
+  return NS_AsyncCopy(aStream, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS,
+                      UDP_PACKET_CHUNK_SIZE);
+}
+
 nsresult
 nsUDPSocket::SetSocketOption(const PRSocketOptionData& aOpt)
 {
   bool onSTSThread = false;
   mSts->IsOnCurrentThread(&onSTSThread);
 
   if (!onSTSThread) {
     // Dispatch to STS thread and re-enter this method there
--- a/netwerk/test/TestUDPSocket.cpp
+++ b/netwerk/test/TestUDPSocket.cpp
@@ -268,25 +268,25 @@ main(int32_t argc, char *argv[])
   NS_ENSURE_SUCCESS(rv, -1);
   client = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
   NS_ENSURE_SUCCESS(rv, -1);
 
   // Create UDPServerListener to process UDP packets
   nsRefPtr<UDPServerListener> serverListener = new UDPServerListener();
 
   // Bind server socket to 0.0.0.0
-  rv = server->Init(0, false);
+  rv = server->Init(0, false, true, 0);
   NS_ENSURE_SUCCESS(rv, -1);
   int32_t serverPort;
   server->GetPort(&serverPort);
   server->AsyncListen(serverListener);
 
   // Bind clinet on arbitrary port
   nsRefPtr<UDPClientListener> clientListener = new UDPClientListener();
-  client->Init(0, false);
+  client->Init(0, false, true, 0);
   client->AsyncListen(clientListener);
 
   // Write data to server
   uint32_t count;
   const uint32_t data = REQUEST;
 
   phase = TEST_OUTPUT_STREAM;
   rv = client->Send(NS_LITERAL_CSTRING("127.0.0.1"), serverPort, (uint8_t*)&data, sizeof(uint32_t), &count);
rename from netwerk/test/unit/test_bug952927.js
rename to netwerk/test/unit/test_udpsocket.js
--- a/netwerk/test/unit/test_bug952927.js
+++ b/netwerk/test/unit/test_udpsocket.js
@@ -1,31 +1,61 @@
-var rawData = new Uint8Array([65,66,67,68]);
-var data = String.fromCharCode.apply(null, rawData);
-
-function UDPSocketListener(){}
-
-UDPSocketListener.prototype = {
-  QueryInterface : XPCOMUtils.generateQI([Ci.nsIUDPSocketListener]),
+/* -*- Mode: Javasript; indent-tab-mode: nil; js-indent-level: 2 -*- */
+/* 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 http://mozilla.org/MPL/2.0/. */
+"use strict";
 
-  onPacketReceived : function(aSocket, aMessage){
-    var mData = String.fromCharCode.apply(null, aMessage.rawData);
-    do_check_eq(mData, data);
-    do_check_eq(mData, aMessage.data);
-    do_test_finished();
-  },
+const HELLO_WORLD = "Hello World";
 
-  onStopListening: function(aSocket, aStatus){}
-};
+add_test(function test_udp_message_raw_data() {
+  do_print("test for nsIUDPMessage.rawData");
 
-
-function run_test(){
-  var socket = Cc["@mozilla.org/network/udp-socket;1"].createInstance(Ci.nsIUDPSocket);
+  let socket = Cc["@mozilla.org/network/udp-socket;1"].createInstance(Ci.nsIUDPSocket);
 
   socket.init(-1, true);
   do_print("Port assigned : " + socket.port);
-  socket.asyncListen(new UDPSocketListener());
+  socket.asyncListen({
+    QueryInterface : XPCOMUtils.generateQI([Ci.nsIUDPSocketListener]),
+    onPacketReceived : function(aSocket, aMessage){
+      let recv_data = String.fromCharCode.apply(null, aMessage.rawData);
+      do_check_eq(recv_data, HELLO_WORLD);
+      do_check_eq(recv_data, aMessage.data);
+      socket.close();
+      run_next_test();
+    },
+    onStopListening: function(aSocket, aStatus){}
+  });
+
+  let rawData = new Uint8Array(HELLO_WORLD.length);
+  for (let i = 0; i < HELLO_WORLD.length; i++) {
+    rawData[i] = HELLO_WORLD.charCodeAt(i);
+  }
+  let written = socket.send("127.0.0.1", socket.port, rawData, rawData.length);
+  do_check_eq(written, HELLO_WORLD.length);
+});
 
-  var written = socket.send("127.0.0.1", socket.port, rawData, rawData.length);
-  do_check_eq(written, data.length);
-  do_test_pending();
+add_test(function test_udp_send_stream() {
+  do_print("test for nsIUDPSocket.sendBinaryStream");
+
+  let socket = Cc["@mozilla.org/network/udp-socket;1"].createInstance(Ci.nsIUDPSocket);
+
+  socket.init(-1, true);
+  socket.asyncListen({
+    QueryInterface : XPCOMUtils.generateQI([Ci.nsIUDPSocketListener]),
+    onPacketReceived : function(aSocket, aMessage){
+      let recv_data = String.fromCharCode.apply(null, aMessage.rawData);
+      do_check_eq(recv_data, HELLO_WORLD);
+      socket.close();
+      run_next_test();
+    },
+    onStopListening: function(aSocket, aStatus){}
+  });
+
+  let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
+  stream.setData(HELLO_WORLD, HELLO_WORLD.length);
+  socket.sendBinaryStream("127.0.0.1", socket.port, stream);
+});
+
+function run_test(){
+  run_next_test();
 }
 
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -144,17 +144,17 @@ skip-if = os == "android"
 [test_bug667818.js]
 [test_bug669001.js]
 [test_bug712914_secinfo_validation.js]
 [test_bug770243.js]
 [test_bug894586.js]
 # Allocating 4GB might actually succeed on 64 bit machines
 skip-if = bits != 32
 [test_bug935499.js]
-[test_bug952927.js]
+[test_udpsocket.js]
 [test_doomentry.js]
 [test_cacheflags.js]
 [test_cache_jar.js]
 [test_channel_close.js]
 [test_compareURIs.js]
 [test_compressappend.js]
 [test_content_encoding_gzip.js]
 [test_content_sniffer.js]