chromium/extensions/test/data/sockets_udp/api/background.js

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

const request = "0100000005320000005hello";

var address;
var bytesSent = 0;
var dataAsString;
var dataRead = [];
var port = -1;
var socketId = 0;
var succeeded = false;
var waitCount = 0;

// Many thanks to Dennis for the StackOverflow answer: http://goo.gl/UDanx
// Since amended to handle BlobBuilder deprecation.
function string2ArrayBuffer(string, callback) {
  var blob = new Blob([string]);
  var f = new FileReader();
  f.onload = function(e) {
    callback(e.target.result);
  };
  f.readAsArrayBuffer(blob);
}

function arrayBuffer2String(buf, callback) {
  var blob = new Blob([new Uint8Array(buf)]);
  var f = new FileReader();
  f.onload = function(e) {
    callback(e.target.result);
  };
  f.readAsText(blob);
}

///////////////////////////////////////////////////////////////////////////////
// Test socket creation
//

var testSocketCreation = function() {
  function onCreate(createInfo) {
    function onGetInfo(info) {
      if (info.localAddress || info.localPort) {
        chrome.test.fail('Unconnected socket should not have local binding');
      }

      chrome.test.assertEq(createInfo.socketId, info.socketId);
      chrome.test.assertEq(false, info.persistent);

      chrome.sockets.udp.close(createInfo.socketId, function() {
        chrome.sockets.udp.getInfo(createInfo.socketId, function(info) {
          chrome.test.assertEq(undefined, info);
          chrome.test.succeed();
        });
      });
    }

    chrome.test.assertTrue(createInfo.socketId > 0);

    // Obtaining socket information before a connect() call should be safe, but
    // return empty values.
    chrome.sockets.udp.getInfo(createInfo.socketId, onGetInfo);
  }

  chrome.sockets.udp.create({}, onCreate);
};

///////////////////////////////////////////////////////////////////////////////
// Test socket send/receive
//

function waitForBlockingOperation() {
  if (++waitCount < 10) {
    setTimeout(waitForBlockingOperation, 1000);
  } else {
    // We weren't able to succeed in the given time.
    chrome.test.fail("Operations didn't complete after " + waitCount + " " +
                     "seconds. Response so far was <" + dataAsString + ">.");
  }
}

var testSending = function() {
  dataRead = "";
  succeeded = false;
  waitCount = 0;
  socketId = 0;
  var localSocketId;

  console.log("testSending");
  setTimeout(waitForBlockingOperation, 1000);
  chrome.sockets.udp.create({}, onCreate);

  function onCreate(socketInfo) {
    console.log("socket created: " + socketInfo.socketId);
    localSocketId = socketId = socketInfo.socketId;
    chrome.test.assertTrue(localSocketId > 0, "failed to create socket");
    chrome.sockets.udp.onReceive.addListener(onReceive);
    chrome.sockets.udp.onReceiveError.addListener(onReceiveError);
    chrome.sockets.udp.bind(localSocketId, "0.0.0.0", 0, onBind);
  }

  function onBind(result) {
    console.log("socket bound to local host");
    chrome.test.assertEq(0, result, "Bind failed with error: " + result);
    if (result < 0)
      return;

    chrome.sockets.udp.getInfo(localSocketId, onGetInfo);
  }

  function onGetInfo(result) {
    console.log("got socket info");
    chrome.test.assertTrue(!!result.localAddress,
                           "Bound socket should always have local address");
    chrome.test.assertTrue(!!result.localPort,
                           "Bound socket should always have local port");

    string2ArrayBuffer(request, onArrayBuffer);
  }

  function onArrayBuffer(arrayBuffer) {
    console.log("sending bytes to echo server: " + arrayBuffer.byteLength);
    chrome.sockets.udp.send(localSocketId, arrayBuffer, address, port,
                            function(sendInfo) {
      chrome.test.assertEq(0, sendInfo.resultCode);
      chrome.test.assertEq(sendInfo.bytesSent, arrayBuffer.byteLength);
    });
  }

  function onReceiveError(info) {
    chrome.test.fail("Socket receive error: " + info.resultCode);
  }

  function onReceive(info) {
    console.log("received bytes on from echo server: " + info.data.byteLength +
      "(" + info.socketId + ")");
    if (localSocketId == info.socketId) {
      arrayBuffer2String(info.data, function(response) {
        dataAsString = response;  // save this for error reporting
        chrome.test.assertEq(request, response);
        chrome.sockets.udp.close(localSocketId, function () {
          chrome.sockets.udp.onReceive.removeListener(onReceive);
          chrome.sockets.udp.onReceiveError.removeListener(onReceiveError);
          succeeded = true;
          chrome.test.succeed();
        });
      });
    }
  }
}

var testSetPaused = function() {
  dataRead = "";
  succeeded = false;
  waitCount = 0;
  socketId = 0;
  var localSocketId = 0;
  var receiveTimer;

  console.log("testSetPaused");
  setTimeout(waitForBlockingOperation, 1000);
  chrome.sockets.udp.create({}, onCreate);

  function onCreate(socketInfo) {
    console.log("socket created: " + socketInfo.socketId);
    localSocketId = socketId = socketInfo.socketId;
    chrome.test.assertTrue(localSocketId > 0, "failed to create socket");

    chrome.sockets.udp.onReceiveError.addListener(onReceiveError);
    chrome.sockets.udp.onReceive.addListener(onReceive);

    chrome.sockets.udp.setPaused(localSocketId, true, function () {
      chrome.sockets.udp.bind(localSocketId, "0.0.0.0", 0, onBind);
    });
  }

  function onBind(result) {
    console.log("socket bound to local host");
    chrome.test.assertEq(0, result, "Bind failed with error: " + result);
    if (result < 0)
      return;

    string2ArrayBuffer(request, onArrayBuffer);
  }

  function onArrayBuffer(arrayBuffer) {
    console.log("sending bytes to echo server: " + arrayBuffer.byteLength);
    chrome.sockets.udp.send(localSocketId, arrayBuffer, address, port,
                            function(sendInfo) {
      chrome.test.assertEq(0, sendInfo.resultCode);
      chrome.test.assertEq(sendInfo.bytesSent, arrayBuffer.byteLength);
      receiveTimer = setTimeout(waitForReceiveEvents, 1000);
    });
  }

  function waitForReceiveEvents() {
    chrome.sockets.udp.close(localSocketId, function () {
      chrome.sockets.udp.onReceive.removeListener(onReceive);
      chrome.sockets.udp.onReceiveError.removeListener(onReceiveError);
      succeeded = true;
      chrome.test.succeed("No data received from echo server!");
    });
  };

  function onReceiveError(info) {
    if (localSocketId == info.socketId) {
      if (receiveTimer)
        clearTimeout(receiveTimer);
      chrome.test.fail("Socket receive error: " + info.resultCode);
    }
  }

  function onReceive(info) {
    console.log("Received data on socket" + "(" + info.socketId + ")");
    if (localSocketId == info.socketId) {
      if (receiveTimer)
        clearTimeout(receiveTimer);
      chrome.test.fail("Should not receive data when socket is paused: " +
                       info.data.byteLength);
    }
  }
}

var testPauseAndThenResume = function () {
  waitCount = 0;
  socketId = 0;

  console.log("testPauseAndThenResume");
  setTimeout(waitForBlockingOperation, 1000);
  chrome.sockets.udp.create({}, onCreate);

  function onCreate(socketInfo) {
    console.log("socket created: " + socketInfo.socketId);
    socketId = socketInfo.socketId;
    chrome.test.assertTrue(socketId > 0, "failed to create socket");

    chrome.sockets.udp.onReceive.addListener(onReceive);

    chrome.sockets.udp.setPaused(socketId, true, () => {
      chrome.sockets.udp.bind(socketId, "0.0.0.0", 0, onBind);
    });
  }

  let sendNumber = 0;
  function send(callback) {
    const encoder = new TextEncoder();
    sendNumber++;
    console.log(`Issuing send request #${sendNumber}`);
    chrome.sockets.udp.send(
      socketId, encoder.encode(request), address, port, callback);
  }

  function onBind(result) {
    console.log("socket bound to local host");
    chrome.test.assertEq(0, result, "Bind failed with error: " + result);
    if (result < 0)
      return;

    send(() => { setTimeout(waitForFirstReceiveToTimeout, 1000); });
  }

  let packetsReceived = 0;
  function waitForFirstReceiveToTimeout() {
    console.log("waitForFirstReceiveToTimeout");
    // No packets should arrive in response to the first send request.
    chrome.test.assertEq(packetsReceived, 0);
    chrome.sockets.udp.setPaused(socketId, false, () => {
      send(() => { setTimeout(waitForSecondReceiveToSucceed, 1000); });
    });
  }

  function waitForSecondReceiveToSucceed() {
    console.log("waitForSecondReceiveToSucceed");
    // After unpausing the socket and issuing one more send request there should
    // be two packets -- echoes from the first and second send respectively.
    chrome.test.assertEq(packetsReceived, 2);
    chrome.test.succeed();
  }

  function onReceive(info) {
    if (socketId == info.socketId) {
      // Packets should only arrive in response to the second send request.
      chrome.test.assertEq(sendNumber, 2);
      packetsReceived++;
    }
  }
}

// Not run as a UDP test due to timeout on multiple platforms.
// https://crbug.com/844402, https://crbug.com/875920.
var testBroadcast = function() {
  var listeningSocketId;
  var sendingSocketId;

  console.log("testBroadcast");
  chrome.sockets.udp.create({}, onCreate);

  function onCreate(socketInfo) {
    console.log("socket created: " + socketInfo.socketId);
    chrome.test.assertTrue(socketInfo.socketId > 0, "failed to create socket");

    if (listeningSocketId == undefined) {
      listeningSocketId = socketInfo.socketId;
      chrome.sockets.udp.onReceive.addListener(onReceive);
      chrome.sockets.udp.onReceiveError.addListener(onReceiveError);
      chrome.sockets.udp.bind(
          listeningSocketId, "0.0.0.0", 8000, onBindListening);
    } else {
      sendingSocketId = socketInfo.socketId;
      chrome.sockets.udp.bind(
          sendingSocketId, "127.0.0.1", 8001, onBindSending);
    }
  }

  function onBindListening(result) {
    chrome.test.assertEq(0, result, "Bind failed with error: " + result);
    if (result < 0) {
      return;
    }

    chrome.sockets.udp.setBroadcast(
        listeningSocketId, true, onSetBroadcastListening);
  }

  function onSetBroadcastListening(result) {
    chrome.test.assertEq(0, result, "Failed to enable broadcast: " + result);
    if (result < 0) {
      return;
    }

    // Create the sending socket.
    chrome.sockets.udp.create({}, onCreate);
  }

  function onBindSending(result) {
    chrome.test.assertEq(0, result, "Bind failed with error: " + result);
    if (result < 0) {
      return;
    }

    chrome.sockets.udp.setBroadcast(
        sendingSocketId, true, onSetBroadcastSending);
  }

  function onSetBroadcastSending(result) {
    chrome.test.assertEq(0, result, "Failed to enable broadcast: " + result);
    if (result < 0) {
      return;
    }

    string2ArrayBuffer("broadcast packet", onArrayBuffer);
  }

  function onArrayBuffer(arrayBuffer) {
    console.log("sending bytes to broadcast: " + arrayBuffer.byteLength);
    chrome.sockets.udp.send(sendingSocketId, arrayBuffer, "127.255.255.255",
                            8000, function(sendInfo) {
      chrome.test.assertEq(0, sendInfo.resultCode);
      chrome.test.assertEq(sendInfo.bytesSent, arrayBuffer.byteLength);
    });
  }

  function onReceiveError(info) {
    chrome.test.fail("Socket receive error: " + info.resultCode);
  }

  function onReceive(info) {
    console.log("Received data on socket" + "(" + info.socketId + ")");
    chrome.test.assertEq(listeningSocketId, info.socketId);
    chrome.test.assertEq("127.0.0.1", info.remoteAddress);
    chrome.test.assertEq(8001, info.remotePort);
    arrayBuffer2String(info.data, function(string) {
      chrome.test.assertEq("broadcast packet", string);
      chrome.test.succeed();
    });
  }
};

///////////////////////////////////////////////////////////////////////////////
// Test driver
//

var onMessageReply = function(message) {
  var parts = message.split(":");
  var test_type = parts[0];
  address = parts[1];
  port = parseInt(parts[2]);
  console.log("Running tests, echo server " +
              address + ":" + port);
  if (test_type == 'multicast') {
    console.log("Running multicast tests");
    chrome.test.runTests([ testMulticast ]);
  } else {
    console.log("Running udp tests");
    chrome.test.runTests([testSocketCreation, testSending, testSetPaused,
      testPauseAndThenResume]);
  }
};

// Find out which protocol we're supposed to test, and which echo server we
// should be using, then kick off the tests.
chrome.test.sendMessage("info_please", onMessageReply);