tor-browser

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

test_peerConnection_threeUnbundledConnections.html (5059B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <script type="application/javascript" src="pc.js"></script>
      5 </head>
      6 <body>
      7 <pre id="test">
      8 <script type="application/javascript">
      9  createHTML({
     10    bug: "1342579",
     11    title: "Unbundled PC connects to two different PCs",
     12    visible: true
     13  });
     14 
     15  const fakeFingerPrint = "a=fingerprint:sha-256 11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11";
     16 
     17  const pc1 = new RTCPeerConnection();
     18  const pc2 = new RTCPeerConnection();
     19  const pc3 = new RTCPeerConnection();
     20 
     21  const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
     22  pc1.onicecandidate = e => {
     23    if (e.candidate) {
     24      if (e.candidate.sdpMid === "1") {
     25        add(pc2, e.candidate, generateErrorCallback())
     26      } else {
     27        add(pc3, e.candidate, generateErrorCallback())
     28      }
     29    }
     30  };
     31  pc2.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
     32  pc3.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
     33 
     34  let ice1Finished, ice2Finished, ice3Finished;
     35  const ice1Done = new Promise(r => ice1Finished = r);
     36  const ice2Done = new Promise(r => ice2Finished = r);
     37  const ice3Done = new Promise(r => ice3Finished = r);
     38 
     39  const icsc = (pc, str, resolve) => {
     40    const state = pc.iceConnectionState;
     41    info(str + " ICE connection state is: " + state);
     42    if (state == "connected") {
     43      ok(true, str + " ICE connected");
     44      resolve();
     45    } else if (state == "failed") {
     46      ok(false, str + " ICE failed")
     47      resolve();
     48    }
     49  };
     50 
     51  pc1.oniceconnectionstatechange = e => icsc(pc1, "PC1", ice1Finished);
     52  pc2.oniceconnectionstatechange = e => icsc(pc2, "PC2", ice2Finished);
     53  pc3.oniceconnectionstatechange = e => icsc(pc3, "PC3", ice3Finished);
     54 
     55 
     56  function combineAnswer(origAnswer, answer) {
     57    const sdplines = origAnswer.sdp.split('\r\n');
     58    const fpIndex = sdplines.findIndex(l => l.match('^a=fingerprint'));
     59    const FP = sdplines[fpIndex];
     60    const audioIndex = sdplines.findIndex(l => l.match(/^m=audio [1-9]/));
     61    const videoIndex = sdplines.findIndex(l => l.match(/^m=video [1-9]/));
     62    if (audioIndex > -1) {
     63      var ss = sdplines.slice(0, audioIndex);
     64      ss.splice(fpIndex, 1);
     65      answer.sessionSection = ss;
     66      const rejectedVideoIndex = sdplines.findIndex(l => l.match('m=video 0'));
     67      var ams = sdplines.slice(audioIndex, rejectedVideoIndex);
     68      ams.push(FP);
     69      ams.push(fakeFingerPrint);
     70      answer.audioMsection = ams;
     71    }
     72    if (videoIndex > -1) {
     73      var vms = sdplines.slice(videoIndex, sdplines.length -1);
     74      vms.push(fakeFingerPrint);
     75      vms.push(FP);
     76      answer.videoMsection = vms;
     77    }
     78    return answer;
     79  }
     80 
     81 runNetworkTest(async () => {
     82    const v1 = createMediaElement('video', 'v1');
     83    const v2 = createMediaElement('video', 'v2');
     84    const v3 = createMediaElement('video', 'v3');
     85 
     86    const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
     87    (v1.srcObject = stream).getTracks().forEach(t => pc1.addTrack(t, stream));
     88 
     89    const stream2 = await navigator.mediaDevices.getUserMedia({ video: true });
     90    (v2.srcObject = stream2).getTracks().forEach(t => pc2.addTrack(t, stream2));
     91 
     92    const stream3 = await navigator.mediaDevices.getUserMedia({ audio: true });
     93    (v3.srcObject = stream3).getTracks().forEach(t => pc3.addTrack(t, stream3));
     94 
     95    const offer = await pc1.createOffer();
     96    await pc1.setLocalDescription(offer);
     97 
     98    //info("Original OFFER: " + JSON.stringify(offer));
     99    offer.sdp = sdputils.removeBundle(offer.sdp);
    100    //info("OFFER w/o BUNDLE: " + JSON.stringify(offer));
    101    const offerAudio = JSON.parse(JSON.stringify(offer));
    102    offerAudio.sdp = offerAudio.sdp.replace('m=video 9', 'm=video 0');
    103    //info("offerAudio: " + JSON.stringify(offerAudio));
    104    const offerVideo = JSON.parse(JSON.stringify(offer));
    105    offerVideo.sdp = offerVideo.sdp.replace('m=audio 9', 'm=audio 0');
    106    //info("offerVideo: " + JSON.stringify(offerVideo));
    107 
    108    // We need to do these in parallel, otherwise pc1 will start firing
    109    // icecandidate events before pc3 is ready.
    110    await Promise.all([pc2.setRemoteDescription(offerVideo), pc3.setRemoteDescription(offerAudio)]);
    111 
    112    const answerVideo = await pc2.createAnswer();
    113    const answerAudio = await pc3.createAnswer();
    114 
    115    const answer = combineAnswer(answerAudio, combineAnswer(answerVideo, {}));
    116    const fakeAnswer = answer.sessionSection.concat(answer.audioMsection, answer.videoMsection).join('\r\n');
    117    info("ANSWER: " + fakeAnswer);
    118 
    119    // We want to do these in parallel, because if we do them seqentially, by the
    120    // time pc3.sLD completes pc2 could have fired icecandidate events, when we
    121    // haven't called pc1.sRD yet.
    122    await Promise.all(
    123      [pc2.setLocalDescription(answerVideo),
    124       pc3.setLocalDescription(answerAudio),
    125       pc1.setRemoteDescription({type: 'answer', sdp: fakeAnswer})]);
    126 
    127    await Promise.all([ice1Done, ice2Done, ice3Done]);
    128 
    129    ok(true, "Connected.");
    130  });
    131 </script>
    132 </pre>
    133 </body>
    134 </html>