910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

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

// It is fine to reuse the same address, because
// the port will be generated unique on this machine.
// multicast addresses are in range 224.0.0.0 to 239.255.255.255.
const multicastGroupAddress = '237.132.100.17';

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 joinGroup(options, ipAddress) {
  try {
    let udpSocket = new UDPSocket(options);
    const {multicastController} = await udpSocket.opened;

    await multicastController.joinGroup(ipAddress);
    await udpSocket.close();
    return 'joinGroup succeeded.';
  } catch (error) {
    return ('joinGroup failed: ' + error);
  }
}

async function joinGroupTwice(options) {
  try {
    let udpSocket = new UDPSocket(options);
    const {multicastController} = await udpSocket.opened;

    await multicastController.joinGroup(multicastGroupAddress);
    await multicastController.joinGroup(multicastGroupAddress);
    await udpSocket.close();
    return 'joinGroupTwice succeeded.';
  } catch (error) {
    return ('joinGroupTwice failed: ' + error);
  }
}

async function leaveGroupAfterJoin(options) {
  try {
    let udpSocket = new UDPSocket(options);
    const {multicastController} = await udpSocket.opened;
    assertEq(multicastController.joinedGroups.length, 0);

    await multicastController.joinGroup(multicastGroupAddress);

    assertEq(multicastController.joinedGroups.length, 1);
    assertEq(multicastController.joinedGroups[0], multicastGroupAddress);

    await multicastController.leaveGroup(multicastGroupAddress);
    assertEq(multicastController.joinedGroups.length, 0);

    await udpSocket.close();
    return 'leaveGroupAfterJoin succeeded.';
  } catch (error) {
    return ('leaveGroupAfterJoin failed: ' + error);
  }
}

async function leaveGroupTwiceAfterJoin(options) {
  try {
    let udpSocket = new UDPSocket(options);
    const {multicastController} = await udpSocket.opened;

    await multicastController.joinGroup(multicastGroupAddress);
    await multicastController.leaveGroup(multicastGroupAddress);
    await multicastController.leaveGroup(multicastGroupAddress);

    await udpSocket.close();
    return 'leaveGroupTwiceAfterJoin succeeded.';
  } catch (error) {
    return ('leaveGroupTwiceAfterJoin failed: ' + error);
  }
}

async function joinGroupAfterClose(options) {
  try {
    let udpSocket = new UDPSocket(options);
    const {multicastController} = await udpSocket.opened;
    await udpSocket.close();

    await multicastController.joinGroup(multicastGroupAddress);
    return 'joinGroupAfterClose succeeded.';
  } catch (error) {
    return ('joinGroupAfterClose failed: ' + error);
  }
}

async function leaveGroupAfterClose(options) {
  try {
    let udpSocket = new UDPSocket(options);
    const {multicastController} = await udpSocket.opened;

    await multicastController.joinGroup(multicastGroupAddress);
    await udpSocket.close();

    await multicastController.leaveGroup(multicastGroupAddress);

    return 'leaveGroupAfterClose succeeded.';
  } catch (error) {
    return ('leaveGroupAfterClose 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 multicastControllerAbsent(options) {
  try {
    let socket = new UDPSocket(options);
    let {multicastController} = await socket.opened;
    let hasMulticast = multicastController != null;

    await socket.close();

    if (hasMulticast) {
      throw new TypeError('multicast must be absent');
    } else {
      return 'multicastControllerAbsent succeeded.';
    }
  } catch (error) {
    return 'multicastControllerAbsent 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 exchangeUdpMulticastPackets() {
  const kRequiredDatagrams = 35;
  const kRequiredBytes = kRequiredDatagrams * (kRequiredDatagrams + 1) / 2;

  try {
    const receiverSocket = new UDPSocket({
      localAddress: '0.0.0.0',
    });
    const {localPort: multicastPort, multicastController} =
        await receiverSocket.opened;
    multicastController.joinGroup(multicastGroupAddress);

    const senderSocket = new UDPSocket({
      remoteAddress: multicastGroupAddress,
      remotePort: multicastPort,
      multicastTimeToLive: 0,
      multicastLoopback: true
    });

    const sendLoopPromise = sendLoop(senderSocket, kRequiredBytes);
    const readLoopPromise = readLoop(receiverSocket, kRequiredBytes);

    await Promise.all([sendLoopPromise, readLoopPromise]);

    await senderSocket.close();
    await receiverSocket.close();

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

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

  try {
    // Create receiver 1.
    const receiverSocket1 = new UDPSocket(
        {localAddress: '0.0.0.0', multicastAllowAddressSharing: true});
    const {
      localPort: multicastPort,
      multicastController: multicastController1
    } = await receiverSocket1.opened;
    multicastController1.joinGroup(multicastGroupAddress);

    // Create receiver 2.
    const receiverSocket2 = new UDPSocket({
      localAddress: '0.0.0.0',
      localPort: multicastPort,
      multicastAllowAddressSharing: true
    });
    const {multicastController: multicastController2} =
        await receiverSocket2.opened;
    multicastController2.joinGroup(multicastGroupAddress);

    const senderSocket = new UDPSocket({
      remoteAddress: multicastGroupAddress,
      remotePort: multicastPort,
      multicastTimeToLive: 0,
      multicastLoopback: true
    });

    const readLoopPromise1 = readLoop(receiverSocket1, kRequiredBytes);
    const readLoopPromise2 = readLoop(receiverSocket2, kRequiredBytes);
    const sendLoopPromise = sendLoop(senderSocket, kRequiredBytes);

    await Promise.all([sendLoopPromise, readLoopPromise1, readLoopPromise2]);

    await senderSocket.close();
    await receiverSocket1.close();
    await receiverSocket2.close();

    return 'exchangeUdpMulticastPackets succeeded.';
  } catch (error) {
    return 'exchangeUdpMulticastPackets 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;
  }
}