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>