| <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> |