chromium/content/test/data/direct_sockets/udp.js

'use strict';

const assertEq = (actual, expected) => {
  if (actual !== expected) {
    throw `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`;
  }
};

async function launchUdpEchoServer(server, requiredBytes, clientAddress, clientPort) {
  let bytesEchoed = 0;

  const { readable, writable } = await server.opened;
  const reader = readable.getReader();
  const writer = writable.getWriter();

  while (bytesEchoed < requiredBytes) {
    const { value: { data, remoteAddress, remotePort }, done } = await reader.read();
    assertEq(done, false);
    assertEq(remoteAddress, clientAddress);
    assertEq(remotePort, clientPort);
    for (let index = 0; index < data.length; index++) {
      assertEq(data[index], bytesEchoed % 256);
      bytesEchoed++;
    }
    await writer.write({ data, remoteAddress, remotePort });
  }

  assertEq(bytesEchoed, requiredBytes);
  reader.releaseLock();
  writer.releaseLock();
}

async function sendLoop(socket, requiredBytes) {
  let bytesWritten = 0;
  let chunkLength = 0;

  const { writable } = await socket.opened;
  const writer = writable.getWriter();

  while (bytesWritten < requiredBytes) {
    chunkLength = Math.min(chunkLength + 1,
                           requiredBytes - bytesWritten);
    let chunk = new Uint8Array(chunkLength);
    for (let index = 0; index < chunkLength; index++) {
      chunk[index] = bytesWritten % 256;
      bytesWritten++;
    }
    await writer.ready;
    await writer.write({ data: chunk });
  }
  assertEq(bytesWritten, requiredBytes);

  writer.releaseLock();
}

async function readLoop(socket, requiredBytes) {
  let bytesRead = 0;

  const { readable } = await socket.opened;
  const reader = readable.getReader();

  while (bytesRead < requiredBytes) {
    const { value: { data }, done } = await reader.read();
    assertEq(done, false);
    for (let index = 0; index < data.length; index++) {
      assertEq(data[index], bytesRead % 256);
      bytesRead++;
    }
  }
  assertEq(bytesRead, requiredBytes);

  reader.releaseLock();
}

async function closeUdp(options) {
  try {
    let udpSocket = new UDPSocket(options);
    await udpSocket.opened;
    await udpSocket.close();
    return 'closeUdp succeeded';
  } catch (error) {
    return ('closeUdp failed: ' + error);
  }
}

async function sendUdpAfterClose(options, requiredBytes) {
  try {
    let udpSocket = new UDPSocket(options);
    let { writable } = await udpSocket.opened;
    await udpSocket.close();

    return await sendLoop(udpSocket, requiredBytes);
  } catch (error) {
    return ('send failed: ' + error);
  }
}

async function readUdpAfterSocketClose(options) {
  try {
    let udpSocket = new UDPSocket(options);
    let { readable, writable } = await udpSocket.opened;
    let reader = readable.getReader();
    let writer = writable.getWriter();
    let rp = reader.read().catch(() => {});
    await reader.cancel();
    await writer.abort();
    await rp;
    return 'readUdpAferSocketClose succeeded.';
  } catch (error) {
    return ('readUdpAfterSocketClose failed: ' + error);
  }
}

async function readUdpAfterStreamClose(options) {
  try {
    let udpSocket = new UDPSocket(options);
    let { readable } = await udpSocket.opened;
    let reader = readable.getReader();
    let rp = reader.read().catch(() => {});
    await reader.cancel();
    let { value, done } = await rp;
    if (!done) {
      return 'Stream is not closed!';
    }
    return 'readUdpAferStreamClose succeeded.';
  } catch (error) {
    return ('readUdpAfterStreamClose failed: ' + error);
  }
}

async function closeUdpWithLockedReadable(options, unlock = false) {
  try {
    let udpSocket = new UDPSocket(options);
    let { readable } = await udpSocket.opened;

    let reader = readable.getReader();

    if (unlock) {
      reader.releaseLock();
    }

    await udpSocket.close();

    if (unlock) {
      await udpSocket.closed;
    }

    return 'closeUdpWithLockedReadable succeeded.';
  } catch (error) {
    return 'closeUdpWithLockedReadable failed: ' + error;
  }
}

async function read(reader) {
  return reader.read().then(() => true, () => false);
}

async function write(writer, value) {
  const encoder = new TextEncoder();
  return writer.write({ data: encoder.encode(value) }).then(() => true, () => false);
}

async function readWriteUdpOnError(socket) {
  try {
    let { readable, writable } = await socket.opened;

    let reader = readable.getReader();
    let writer = writable.getWriter();

    let rp = read(reader);
    let wp = write(writer, '_');

    let [read_request_success, write_request_success] = await Promise.all([rp, wp]);

    if (!read_request_success && !write_request_success) {
      return 'readWriteUdpOnError succeeded.';
    } else {
      throw new TypeError(`read_request_success = ${read_request_success}, write_request_success = ${write_request_success}`);
    }
  } catch (error) {
    return 'readWriteUdpOnError failed: ' + error;
  }
}

async function exchangeUdpPacketsBetweenClientAndServer() {
  const kRequiredDatagrams = 35;
  const kRequiredBytes =
    kRequiredDatagrams * (kRequiredDatagrams + 1) / 2;

  try {
    // |localPort| is intentionally omitted so that the OS will pick one itself.
    const serverSocket = new UDPSocket({ localAddress: "127.0.0.1" });
    const { localPort: serverSocketPort } = await serverSocket.opened;

    // Connect a client to the server.
    const clientSocket = new UDPSocket({
      remoteAddress: "127.0.0.1",
      remotePort: serverSocketPort
    });
    const { localAddress: clientLocalAddress, localPort: clientLocalPort } = await clientSocket.opened;

    launchUdpEchoServer(serverSocket, kRequiredBytes, clientLocalAddress, clientLocalPort);
    sendLoop(clientSocket, kRequiredBytes);
    await readLoop(clientSocket, kRequiredBytes);

    await clientSocket.close();
    await serverSocket.close();

    return "exchangeUdpPacketsBetweenClientAndServer succeeded.";
  } catch (error) {
    return "exchangeUdpPacketsBetweenClientAndServer failed: " + error;
  }
}

async function testUdpMessageConfiguration(socketOptions, message) {
  try {
    const socket = new UDPSocket(socketOptions);
    const { writable } = await socket.opened;
    const writer = writable.getWriter();

    await writer.write(message);
    return "testUdpMessageConfiguration succeeded.";
  } catch (error) {
    return "testUdpMessageConfiguration failed: " + error;
  }
}