1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168

content / test / data / media / peerconnection-setConfiguration.html [blame]

<html>
<head>
  <script type="text/javascript" src="webrtc_test_utilities.js"></script>
  <script type="text/javascript" src="webrtc_test_common.js"></script>
  <script type="text/javascript">
  $ = function(id) {
    return document.getElementById(id);
  };

  var gPeerConnection = null;
  var gCertificate = null;

  // This test creates and sets three offers, calling setConfiguration in
  // between each offer, expecting an ICE restart to be triggered by the next
  // offer.
  async function testSetConfiguration() {
    gPeerConnection = new RTCPeerConnection(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:0});
    // Change ICE candidate pool size, which will succeed before
    // setLocalDescription is called.
    gPeerConnection.setConfiguration(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    // Now test successful cases of setConfiguration. Changes should trigger an
    // ICE restart in the next offer. To do this, first we need to trigger an
    // initial ICE gathering phase and wait until it completes.

    function waitForState(state) {
      return new Promise((resolve) => {
        gPeerConnection.onicegatheringstatechange = (candidate) => {
          if (gPeerConnection.iceGatheringState === state) {
            resolve();
          }
        };
      });
    }

    let onCompletePromise = waitForState('complete');
    await createOfferAndSetLocalDescription();
    await onCompletePromise;
    onCompletePromise = waitForState('complete');
    // Policy changed.
    gPeerConnection.setConfiguration(
        {iceServers:[], iceTransportPolicy:'relay', bundlePolicy:'balanced',
          rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    await createOfferAndSetLocalDescription();
    await onCompletePromise;
    // Only wait for 'gathering', since it will take a while for the requests to
    // 'foo.invalid' to time out.
    const onGatheringPromise = waitForState('gathering');
    // Servers changed.
    gPeerConnection.setConfiguration(
        {iceServers:[{urls:'stun:foo.invalid'}], iceTransportPolicy:'all',
          bundlePolicy:'balanced', rtcpMuxPolicy:'require', certificates:[],
          iceCandidatePoolSize:1});
    await createOfferAndSetLocalDescription();
    await onGatheringPromise;
    return logSuccess();
  }

  async function testSetConfigurationErrors() {
    // Generate certificate so we can test the InvalidModificationError from
    // attempting to change certificates.
    let certificate;
    try {
      certificate = await RTCPeerConnection.generateCertificate(
        { name:'ECDSA', namedCurve:'P-256' });
    } catch {
      throw new Error('Failed to generate certificate.');
    }
    gCertificate = certificate;
    return continueTestSetConfigurationErrors();
  }

  // Continued after certificate generated.
  async function continueTestSetConfigurationErrors() {
    gPeerConnection = new RTCPeerConnection(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    // If bundlePolicy, rtcpMuxPolicy or certificates are changed, an
    // InvalidModificationError should be thrown.
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'max-bundle', rtcpMuxPolicy:'require',
                  certificates:[], iceCandidatePoolSize:1});
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'balanced', rtcpMuxPolicy:'negotiate',
                  certificates:[]});
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'balanced', rtcpMuxPolicy:'require',
                  certificates:[gCertificate], iceCandidatePoolSize:1});
    // Failure to parse URL should result in SyntaxError.
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[{url:'stunnnn:foo.invalid'}],
                  iceTransportPolicy:'all', bundlePolicy:'max-bundle',
                  rtcpMuxPolicy:'require', certificates:[],
                  iceCandidatePoolSize:1});
    // TURN server with missing username should result in InvalidAccessError.
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[{url:'turn:foo.invalid'}],
                  iceTransportPolicy:'all', bundlePolicy:'max-bundle',
                  rtcpMuxPolicy:'require', certificates:[],
                  iceCandidatePoolSize:1});
    // Sanity check that a configuration can be successfully set, and thus
    // there's not something unexpected causing the above exceptions.
    gPeerConnection.setConfiguration(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    // Lastly: only after applying a local description, changing the candidate
    // pool size is not allowed.
    let offer;
    try {
      offer = await gPeerConnection.createOffer({offerToReceiveAudio:1});
    } catch {
      throw new Error('Failed to generate offer.');
    }

    console.log("Setting offer:\n" + offer.sdp);
    try {
      await gPeerConnection.setLocalDescription(offer);
    } catch {
      throw new Error('Failed to set local description.');
    }

    // Pool size absent, which means it should default to 0, which is
    // different than its current value of 1.
    assertThrows(gPeerConnection.setConfiguration,
                  {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'balanced', rtcpMuxPolicy:'require',
                  certificates:[]});
    return logSuccess();
  }

  function assertThrows(func) {
    try {
      func.apply(arguments.slice(start=1));
    } catch (e) {
      return;
    }
    throw new Error('Expected exception to be thrown by: ' + func);
  }

  // Helper function to create and apply offer.
  async function createOfferAndSetLocalDescription() {
    let offer;
    try {
    offer = await gPeerConnection.createOffer({offerToReceiveAudio:1});
    } catch {
      throw new Error('Failed to generate offer.');
    }

    console.log("Setting offer:\n" + offer.sdp);

    try {
      return gPeerConnection.setLocalDescription(offer);
    } catch {
      throw new Error('Failed to set local description.');
    }
  }

  </script>
</head>
<body>
</body>
</html>