tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

candidate-exchange.https.html (8285B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <title>Candidate exchange</title>
      5 <meta name=timeout content=long>
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="../RTCPeerConnection-helper.js"></script>
      9 </head>
     10 <body>
     11 <script>
     12 
     13 class StateLogger {
     14  constructor(source, eventname, field) {
     15    source.addEventListener(eventname, event => {
     16      this.events.push(source[field]);
     17    });
     18    this.events = [source[field]];
     19  }
     20 }
     21 
     22 class IceStateLogger extends StateLogger {
     23  constructor(source) {
     24    super(source, 'iceconnectionstatechange', 'iceConnectionState');
     25  }
     26 }
     27 
     28 promise_test(async t => {
     29  const pc1 = new RTCPeerConnection();
     30  const pc2 = new RTCPeerConnection();
     31  t.add_cleanup(() => pc1.close());
     32  t.add_cleanup(() => pc2.close());
     33  pc1.createDataChannel('datachannel');
     34  pc1IceStates = new IceStateLogger(pc1);
     35  pc2IceStates = new IceStateLogger(pc1);
     36  exchangeIceCandidates(pc1, pc2);
     37  await exchangeOfferAnswer(pc1, pc2);
     38  // Note - it's been claimed that this state sometimes jumps straight
     39  // to "completed". If so, this test should be flaky.
     40  await waitForIceStateChange(pc1, ['connected']);
     41  assert_array_equals(pc1IceStates.events, ['new', 'checking', 'connected']);
     42  assert_array_equals(pc2IceStates.events, ['new', 'checking', 'connected']);
     43 }, 'Two way ICE exchange works');
     44 
     45 promise_test(async t => {
     46  const pc1 = new RTCPeerConnection();
     47  const pc2 = new RTCPeerConnection();
     48  t.add_cleanup(() => pc1.close());
     49  t.add_cleanup(() => pc2.close());
     50  pc1IceStates = new IceStateLogger(pc1);
     51  pc2IceStates = new IceStateLogger(pc1);
     52  let candidates = [];
     53  pc1.createDataChannel('datachannel');
     54  pc1.onicecandidate = e => {
     55    candidates.push(e.candidate);
     56  }
     57  // Candidates from PC2 are not delivered to pc1, so pc1 will use
     58  // peer-reflexive candidates.
     59  await exchangeOfferAnswer(pc1, pc2);
     60  const waiter = waitForIceGatheringState(pc1, ['complete']);
     61  await waiter;
     62  for (const candidate of candidates) {
     63    if (candidate) {
     64      pc2.addIceCandidate(candidate);
     65    }
     66  }
     67  await Promise.all([waitForIceStateChange(pc1, ['connected', 'completed']),
     68                     waitForIceStateChange(pc2, ['connected', 'completed'])]);
     69  const candidate_pair = pc1.sctp.transport.iceTransport.getSelectedCandidatePair();
     70  assert_equals(candidate_pair.local.type, 'host');
     71  assert_equals(candidate_pair.remote.type, 'prflx');
     72  assert_array_equals(pc1IceStates.events, ['new', 'checking', 'connected']);
     73  assert_array_equals(pc2IceStates.events, ['new', 'checking', 'connected']);
     74 }, 'Adding only caller -> callee candidates gives a connection');
     75 
     76 promise_test(async t => {
     77  const pc1 = new RTCPeerConnection();
     78  const pc2 = new RTCPeerConnection();
     79  t.add_cleanup(() => pc1.close());
     80  t.add_cleanup(() => pc2.close());
     81  pc1IceStates = new IceStateLogger(pc1);
     82  pc2IceStates = new IceStateLogger(pc1);
     83  let candidates = [];
     84  pc1.createDataChannel('datachannel');
     85  pc2.onicecandidate = e => {
     86    candidates.push(e.candidate);
     87  }
     88  // Candidates from pc1 are not delivered to pc2.  so pc2 will use
     89  // peer-reflexive candidates.
     90  await exchangeOfferAnswer(pc1, pc2);
     91  const waiter = waitForIceGatheringState(pc2, ['complete']);
     92  await waiter;
     93  for (const candidate of candidates) {
     94    if (candidate) {
     95      pc1.addIceCandidate(candidate);
     96    }
     97  }
     98  await Promise.all([waitForIceStateChange(pc1, ['connected', 'completed']),
     99                     waitForIceStateChange(pc2, ['connected', 'completed'])]);
    100  const candidate_pair = pc2.sctp.transport.iceTransport.getSelectedCandidatePair();
    101  assert_equals(candidate_pair.local.type, 'host');
    102  assert_equals(candidate_pair.remote.type, 'prflx');
    103  assert_array_equals(pc1IceStates.events, ['new', 'checking', 'connected']);
    104  assert_array_equals(pc2IceStates.events, ['new', 'checking', 'connected']);
    105 }, 'Adding only callee -> caller candidates gives a connection');
    106 
    107 promise_test(async t => {
    108  const pc1 = new RTCPeerConnection();
    109  const pc2 = new RTCPeerConnection();
    110  t.add_cleanup(() => pc1.close());
    111  t.add_cleanup(() => pc2.close());
    112  pc1IceStates = new IceStateLogger(pc1);
    113  pc2IceStates = new IceStateLogger(pc1);
    114  let pc2ToPc1Candidates = [];
    115  pc1.createDataChannel('datachannel');
    116  pc2.onicecandidate = e => {
    117    pc2ToPc1Candidates.push(e.candidate);
    118    // This particular test verifies that candidates work
    119    // properly if added from the pc2 onicecandidate event.
    120    if (!e.candidate) {
    121      for (const candidate of pc2ToPc1Candidates) {
    122        if (candidate) {
    123          pc1.addIceCandidate(candidate);
    124        }
    125      }
    126    }
    127  }
    128  // Candidates from |pc1| are not delivered to |pc2|. |pc2| will use
    129  // peer-reflexive candidates.
    130  await exchangeOfferAnswer(pc1, pc2);
    131  await Promise.all([waitForIceStateChange(pc1, ['connected', 'completed']),
    132                     waitForIceStateChange(pc2, ['connected', 'completed'])]);
    133  const candidate_pair = pc2.sctp.transport.iceTransport.getSelectedCandidatePair();
    134  assert_equals(candidate_pair.local.type, 'host');
    135  assert_equals(candidate_pair.remote.type, 'prflx');
    136  assert_array_equals(pc1IceStates.events, ['new', 'checking', 'connected']);
    137  assert_array_equals(pc2IceStates.events, ['new', 'checking', 'connected']);
    138 }, 'Adding callee -> caller candidates from end-of-candidates gives a connection');
    139 
    140 promise_test(async t => {
    141  const pc1 = new RTCPeerConnection();
    142  const pc2 = new RTCPeerConnection();
    143  t.add_cleanup(() => pc1.close());
    144  t.add_cleanup(() => pc2.close());
    145  pc1IceStates = new IceStateLogger(pc1);
    146  pc2IceStates = new IceStateLogger(pc1);
    147  let pc1ToPc2Candidates = [];
    148  let pc2ToPc1Candidates = [];
    149  pc1.createDataChannel('datachannel');
    150  pc1.onicecandidate = e => {
    151    pc1ToPc2Candidates.push(e.candidate);
    152  }
    153  pc2.onicecandidate = e => {
    154    pc2ToPc1Candidates.push(e.candidate);
    155  }
    156  const offer = await pc1.createOffer();
    157  await Promise.all([pc1.setLocalDescription(offer),
    158                     pc2.setRemoteDescription(offer)]);
    159  const answer = await pc2.createAnswer();
    160  await waitForIceGatheringState(pc1, ['complete']);
    161  await pc2.setLocalDescription(answer).then(() => {
    162    for (const candidate of pc1ToPc2Candidates) {
    163      if (candidate) {
    164        pc2.addIceCandidate(candidate);
    165      }
    166    }
    167  });
    168  await waitForIceGatheringState(pc2, ['complete']);
    169  pc1.setRemoteDescription(answer).then(async () => {
    170    for (const candidate of pc2ToPc1Candidates) {
    171      if (candidate) {
    172        await pc1.addIceCandidate(candidate);
    173      }
    174    }
    175  });
    176  await Promise.all([waitForIceStateChange(pc1, ['connected', 'completed']),
    177                     waitForIceStateChange(pc2, ['connected', 'completed'])]);
    178  const candidate_pair =
    179        pc1.sctp.transport.iceTransport.getSelectedCandidatePair();
    180  assert_equals(candidate_pair.local.type, 'host');
    181  // When we supply remote candidates, we expect a jump to the 'host' candidate,
    182  // but it might also remain as 'prflx'.
    183  assert_true(candidate_pair.remote.type == 'host' ||
    184              candidate_pair.remote.type == 'prflx');
    185  assert_array_equals(pc1IceStates.events, ['new', 'checking', 'connected']);
    186  assert_array_equals(pc2IceStates.events, ['new', 'checking', 'connected']);
    187 }, 'Explicit offer/answer exchange gives a connection');
    188 
    189 promise_test(async t => {
    190  const pc1 = new RTCPeerConnection();
    191  t.add_cleanup(() => pc1.close());
    192  pc1.createDataChannel('datachannel');
    193  pc1.onicecandidate = assert_unreached;
    194  const offer = await pc1.createOffer();
    195  await pc1.setLocalDescription(offer);
    196  await new Promise(resolve => {
    197    pc1.onicecandidate = resolve;
    198  });
    199 }, 'Candidates always arrive after setLocalDescription(offer) resolves');
    200 
    201 promise_test(async t => {
    202  const pc1 = new RTCPeerConnection();
    203  const pc2 = new RTCPeerConnection();
    204  t.add_cleanup(() => pc1.close());
    205  t.add_cleanup(() => pc2.close());
    206  pc1.createDataChannel('datachannel');
    207  pc2.onicecandidate = assert_unreached;
    208  const offer = await pc1.createOffer();
    209  await pc2.setRemoteDescription(offer);
    210  await pc2.setLocalDescription(await pc2.createAnswer());
    211  await new Promise(resolve => {
    212    pc2.onicecandidate = resolve;
    213  });
    214 }, 'Candidates always arrive after setLocalDescription(answer) resolves');
    215 
    216 </script>
    217 </body>
    218 </html>