Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  iceTestUtils.js   Sprache: JAVA

 
/* 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";

// This is mostly so test_peerConnection_gatherWithStun300.html and
// test_peerConnection_gatherWithStun300IPv6 can share this code. I would have
// put the ipv6 test code in the same file, but our ipv6 tester support is
// inconsistent enough that we need to be able to track the ipv6 test
// separately.

async function findStatsRelayCandidates(pc, protocol) {
  const stats = await pc.getStats();
  return [...stats.values()].filter(
    v =>
      v.type == "local-candidate" &&
      v.candidateType == "relay" &&
      v.relayProtocol == protocol
  );
}

// Trickles candidates if pcDst is set, and resolves the candidate list
async function trickleIce(pc, pcDst) {
  const candidates = [],
    addCandidatePromises = [];
  while (true) {
    const { candidate } = await new Promise(r =>
      pc.addEventListener("icecandidate", r, { once: true })
    );
    if (!candidate) {
      break;
    }
    candidates.push(candidate);
    if (pcDst) {
      addCandidatePromises.push(pcDst.addIceCandidate(candidate));
    }
  }
  await Promise.all(addCandidatePromises);
  return candidates;
}

async function gather(pc) {
  if (pc.signalingState == "stable") {
    await pc.setLocalDescription(
      await pc.createOffer({ offerToReceiveAudio: true })
    );
  } else if (pc.signalingState == "have-remote-offer") {
    await pc.setLocalDescription();
  }

  return trickleIce(pc);
}

async function gatherWithTimeout(pc, timeout, context) {
  const throwOnTimeout = async () => {
    await wait(timeout);
    throw new Error(
      `Gathering did not complete within ${timeout} ms with ${context}`
    );
  };

  return Promise.race([gather(pc), throwOnTimeout()]);
}

async function iceConnected(pc) {
  return new Promise((resolve, reject) => {
    pc.addEventListener("iceconnectionstatechange", () => {
      if (["connected""completed"].includes(pc.iceConnectionState)) {
        resolve();
      } else if (pc.iceConnectionState == "failed") {
        reject(new Error(`ICE failed`));
      }
    });
  });
}

async function dtlsConnected(pc) {
  return new Promise((resolve, reject) => {
    pc.addEventListener("connectionstatechange", () => {
      if (["connected""completed"].includes(pc.connectionState)) {
        resolve();
      } else if (pc.connectionState == "failed") {
        reject(new Error(`Connection failed`));
      }
    });
  });
}

// Set up trickle, but does not wait for it to complete. Can be used by itself
// in cases where we do not expect any new candidates, but want to still set up
// the signal handling in case new candidates _do_ show up.
async function connectNoTrickleWait(offerer, answerer, timeout, context) {
  return connect(offerer, answerer, timeout, context, true);
}

async function connect(
  offerer,
  answerer,
  timeout,
  context,
  noTrickleWait = false,
  waitForDtls = false
) {
  const trickle1 = trickleIce(offerer, answerer);
  const trickle2 = trickleIce(answerer, offerer);
  try {
    const offer = await offerer.createOffer({ offerToReceiveAudio: true });
    await offerer.setLocalDescription(offer);
    await answerer.setRemoteDescription(offer);
    const answer = await answerer.createAnswer();
    await Promise.all([
      offerer.setRemoteDescription(answer),
      answerer.setLocalDescription(answer),
    ]);

    const throwOnTimeout = async () => {
      if (timeout) {
        await wait(timeout);
        throw new Error(
          `ICE did not complete within ${timeout} ms with ${context}`
        );
      }
    };

    const connectionPromises = waitForDtls
      ? [dtlsConnected(offerer), dtlsConnected(answerer)]
      : [iceConnected(offerer), iceConnected(answerer)];

    await Promise.race([
      Promise.all(connectionPromises),
      throwOnTimeout(timeout, context),
    ]);
  } finally {
    if (!noTrickleWait) {
      // TODO(bug 1751509): For now, we need to let gathering finish before we
      // proceed, because there are races in ICE restart wrt gathering state.
      await Promise.all([trickle1, trickle2]);
    }
  }
}

function isV6HostCandidate(candidate) {
  const fields = candidate.candidate.split(" ");
  const type = fields[7];
  const ipAddress = fields[4];
  return type == "host" && ipAddress.includes(":");
}

async function ipv6Supported() {
  const pc = new RTCPeerConnection();
  const candidates = await gatherWithTimeout(pc, 8000);
  info(`baseline candidates: ${JSON.stringify(candidates)}`);
  pc.close();
  return candidates.some(isV6HostCandidate);
}

function makeContextString(iceServers) {
  const currentRedirectAddress = SpecialPowers.getCharPref(
    "media.peerconnection.nat_simulator.redirect_address",
    ""
  );
  const currentRedirectTargets = SpecialPowers.getCharPref(
    "media.peerconnection.nat_simulator.redirect_targets",
    ""
  );
  return `redirect rule: ${currentRedirectAddress}=>${currentRedirectTargets} iceServers: ${JSON.stringify(
    iceServers
  )}`;
}

async function checkSrflx(iceServers) {
  const context = makeContextString(iceServers);
  info(`checkSrflx ${context}`);
  const pc = new RTCPeerConnection({
    iceServers,
    bundlePolicy: "max-bundle"// Avoids extra candidates
  });
  const candidates = await gatherWithTimeout(pc, 8000, context);
  const srflxCandidates = candidates.filter(c => c.candidate.includes("srflx"));
  info(`candidates: ${JSON.stringify(srflxCandidates)}`);
  // TODO(bug 1339203): Once we support rtcpMuxPolicy, set it to "require" to
  // result in a single srflx candidate
  is(
    srflxCandidates.length,
    2,
    `Should have two srflx candidates with ${context}`
  );
  pc.close();
}

async function checkNoSrflx(iceServers) {
  const context = makeContextString(iceServers);
  info(`checkNoSrflx ${context}`);
  const pc = new RTCPeerConnection({
    iceServers,
    bundlePolicy: "max-bundle"// Avoids extra candidates
  });
  const candidates = await gatherWithTimeout(pc, 8000, context);
  const srflxCandidates = candidates.filter(c => c.candidate.includes("srflx"));
  info(`candidates: ${JSON.stringify(srflxCandidates)}`);
  is(
    srflxCandidates.length,
    0,
    `Should have no srflx candidates with ${context}`
  );
  pc.close();
}

async function checkRelayUdp(iceServers) {
  const context = makeContextString(iceServers);
  info(`checkRelayUdp ${context}`);
  const pc = new RTCPeerConnection({
    iceServers,
    bundlePolicy: "max-bundle"// Avoids extra candidates
  });
  const candidates = await gatherWithTimeout(pc, 8000, context);
  const relayCandidates = candidates.filter(c => c.candidate.includes("relay"));
  info(`candidates: ${JSON.stringify(relayCandidates)}`);
  // TODO(bug 1339203): Once we support rtcpMuxPolicy, set it to "require" to
  // result in a single relay candidate
  is(
    relayCandidates.length,
    2,
    `Should have two relay candidates with ${context}`
  );
  // It would be nice if RTCIceCandidate had a field telling us what the
  // "related protocol" is (similar to relatedAddress and relatedPort).
  // Because there is no such thing, we need to go through the stats API,
  // which _does_ have that information.
  is(
    (await findStatsRelayCandidates(pc, "tcp")).length,
    0,
    `No TCP relay candidates should be present with ${context}`
  );
  pc.close();
}

async function checkRelayTcp(iceServers) {
  const context = makeContextString(iceServers);
  info(`checkRelayTcp ${context}`);
  const pc = new RTCPeerConnection({
    iceServers,
    bundlePolicy: "max-bundle"// Avoids extra candidates
  });
  const candidates = await gatherWithTimeout(pc, 8000, context);
  const relayCandidates = candidates.filter(c => c.candidate.includes("relay"));
  info(`candidates: ${JSON.stringify(relayCandidates)}`);
  // TODO(bug 1339203): Once we support rtcpMuxPolicy, set it to "require" to
  // result in a single relay candidate
  is(
    relayCandidates.length,
    2,
    `Should have two relay candidates with ${context}`
  );
  // It would be nice if RTCIceCandidate had a field telling us what the
  // "related protocol" is (similar to relatedAddress and relatedPort).
  // Because there is no such thing, we need to go through the stats API,
  // which _does_ have that information.
  is(
    (await findStatsRelayCandidates(pc, "udp")).length,
    0,
    `No UDP relay candidates should be present with ${context}`
  );
  pc.close();
}

async function checkRelayUdpTcp(iceServers) {
  const context = makeContextString(iceServers);
  info(`checkRelayUdpTcp ${context}`);
  const pc = new RTCPeerConnection({
    iceServers,
    bundlePolicy: "max-bundle"// Avoids extra candidates
  });
  const candidates = await gatherWithTimeout(pc, 8000, context);
  const relayCandidates = candidates.filter(c => c.candidate.includes("relay"));
  info(`candidates: ${JSON.stringify(relayCandidates)}`);
  // TODO(bug 1339203): Once we support rtcpMuxPolicy, set it to "require" to
  // result in a single relay candidate each for UDP and TCP
  is(
    relayCandidates.length,
    4,
    `Should have two relay candidates for each protocol with ${context}`
  );
  // It would be nice if RTCIceCandidate had a field telling us what the
  // "related protocol" is (similar to relatedAddress and relatedPort).
  // Because there is no such thing, we need to go through the stats API,
  // which _does_ have that information.
  is(
    (await findStatsRelayCandidates(pc, "udp")).length,
    2,
    `Two UDP relay candidates should be present with ${context}`
  );
  // TODO(bug 1705563): This is 1 because of bug 1705563
  is(
    (await findStatsRelayCandidates(pc, "tcp")).length,
    1,
    `One TCP relay candidates should be present with ${context}`
  );
  pc.close();
}

async function checkNoRelay(iceServers) {
  const context = makeContextString(iceServers);
  info(`checkNoRelay ${context}`);
  const pc = new RTCPeerConnection({
    iceServers,
    bundlePolicy: "max-bundle"// Avoids extra candidates
  });
  const candidates = await gatherWithTimeout(pc, 8000, context);
  const relayCandidates = candidates.filter(c => c.candidate.includes("relay"));
  info(`candidates: ${JSON.stringify(relayCandidates)}`);
  is(
    relayCandidates.length,
    0,
    `Should have no relay candidates with ${context}`
  );
  pc.close();
}

function gatheringStateReached(object, state) {
  if (object instanceof RTCIceTransport) {
    return new Promise(r =>
      object.addEventListener("gatheringstatechange"function listener() {
        if (object.gatheringState == state) {
          object.removeEventListener("gatheringstatechange", listener);
          r(state);
        }
      })
    );
  } else if (object instanceof RTCPeerConnection) {
    return new Promise(r =>
      object.addEventListener("icegatheringstatechange"function listener() {
        if (object.iceGatheringState == state) {
          object.removeEventListener("icegatheringstatechange", listener);
          r(state);
        }
      })
    );
  } else {
    throw "First parameter is neither an RTCIceTransport nor an RTCPeerConnection";
  }
}

function nextGatheringState(object) {
  if (object instanceof RTCIceTransport) {
    return new Promise(resolve =>
      object.addEventListener(
        "gatheringstatechange",
        () => resolve(object.gatheringState),
        { once: true }
      )
    );
  } else if (object instanceof RTCPeerConnection) {
    return new Promise(resolve =>
      object.addEventListener(
        "icegatheringstatechange",
        () => resolve(object.iceGatheringState),
        { once: true }
      )
    );
  } else {
    throw "First parameter is neither an RTCIceTransport nor an RTCPeerConnection";
  }
}

function emptyCandidate(pc) {
  return new Promise(r =>
    pc.addEventListener("icecandidate"function listener(e) {
      if (e.candidate && e.candidate.candidate == "") {
        pc.removeEventListener("icecandidate", listener);
        r(e);
      }
    })
  );
}

function nullCandidate(pc) {
  return new Promise(r =>
    pc.addEventListener("icecandidate"function listener(e) {
      if (!e.candidate) {
        pc.removeEventListener("icecandidate", listener);
        r(e);
      }
    })
  );
}

function connectionStateReached(object, state) {
  if (object instanceof RTCIceTransport || object instanceof RTCDtlsTransport) {
    return new Promise(resolve =>
      object.addEventListener("statechange"function listener() {
        if (object.state == state) {
          object.removeEventListener("statechange", listener);
          resolve(state);
        }
      })
    );
  } else if (object instanceof RTCPeerConnection) {
    return new Promise(resolve =>
      object.addEventListener("connectionstatechange"function listener() {
        if (object.connectionState == state) {
          object.removeEventListener("connectionstatechange", listener);
          resolve(state);
        }
      })
    );
  } else {
    throw "First parameter is neither an RTCIceTransport, an RTCDtlsTransport, nor an RTCPeerConnection";
  }
}

function nextConnectionState(object) {
  if (object instanceof RTCIceTransport || object instanceof RTCDtlsTransport) {
    return new Promise(resolve =>
      object.addEventListener("statechange", () => resolve(object.state), {
        once: true,
      })
    );
  } else if (object instanceof RTCPeerConnection) {
    return new Promise(resolve =>
      object.addEventListener(
        "connectionstatechange",
        () => resolve(object.connectionState),
        { once: true }
      )
    );
  } else {
    throw "First parameter is neither an RTCIceTransport, an RTCDtlsTransport, nor an RTCPeerConnection";
  }
}

function nextIceConnectionState(pc) {
  if (pc instanceof RTCPeerConnection) {
    return new Promise(resolve =>
      pc.addEventListener(
        "iceconnectionstatechange",
        () => resolve(pc.iceConnectionState),
        { once: true }
      )
    );
  } else {
    throw "First parameter is not an RTCPeerConnection";
  }
}

Messung V0.5
C=92 H=97 G=94

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge