Browse Source

fix(rn,polyfills) remove no longer needed hack

With react-native-webrtc 1.89.2 the remote SDP is properly updated before
onaddstream is fired so it's no longer needed.

Also, for readability, IPv6 address synthesis has been moved to a standalone
utils file.
master
Saúl Ibarra Corretgé 4 years ago
parent
commit
eb16f93153

+ 13
- 344
react/features/mobile/polyfills/RTCPeerConnection.js View File

@@ -1,352 +1,21 @@
1 1
 // @flow
2 2
 
3
-import { NativeModules } from 'react-native';
4
-import { RTCPeerConnection, RTCSessionDescription } from 'react-native-webrtc';
3
+import { RTCPeerConnection as PC } from 'react-native-webrtc';
5 4
 
6
-/* eslint-disable no-unused-vars */
7
-
8
-// Address families.
9
-const AF_INET6 = 30; /* IPv6 */
10
-
11
-// Protocols (RFC 1700)
12
-const IPPROTO_TCP = 6; /* tcp */
13
-const IPPROTO_UDP = 17; /* user datagram protocol */
14
-
15
-// Protocol families, same as address families for now.
16
-const PF_INET6 = AF_INET6;
17
-
18
-const SOCK_DGRAM = 2; /* datagram socket */
19
-const SOCK_STREAM = 1; /* stream socket */
20
-
21
-/* eslint-enable no-unused-vars */
22
-
23
-// XXX At the time of this writing extending RTCPeerConnection using ES6 'class'
24
-// and 'extends' causes a runtime error related to the attempt to define the
25
-// onaddstream property setter. The error mentions that babelHelpers.set is
26
-// undefined which appears to be a thing inside React Native's packager. As a
27
-// workaround, extend using the pre-ES6 way.
28
-
29
-/**
30
- * The RTCPeerConnection provided by react-native-webrtc fires onaddstream
31
- * before it remembers remotedescription (and thus makes it available to API
32
- * clients). Because that appears to be a problem for lib-jitsi-meet which has
33
- * been successfully running on Chrome, Firefox and others for a very long
34
- * time, attempt to meet its expectations (by extending RTCPPeerConnection).
35
- *
36
- * @class
37
- */
38
-export default function _RTCPeerConnection(...args: any[]) {
39
-
40
-    /* eslint-disable indent, no-invalid-this */
41
-
42
-    RTCPeerConnection.apply(this, args);
43
-
44
-    this.onaddstream = (...args) => // eslint-disable-line no-shadow
45
-        (this._onaddstreamQueue
46
-                ? this._queueOnaddstream
47
-                : this._invokeOnaddstream)
48
-            .apply(this, args);
49
-
50
-    // Shadow RTCPeerConnection's onaddstream but after _RTCPeerConnection has
51
-    // assigned to the property in question. Defining the property on
52
-    // _RTCPeerConnection's prototype may (or may not, I don't know) work but I
53
-    // don't want to try because the following approach appears to work and I
54
-    // understand it.
55
-
56
-    // $FlowFixMe
57
-    Object.defineProperty(this, 'onaddstream', {
58
-        configurable: true,
59
-        enumerable: true,
60
-        get() {
61
-            return this._onaddstream;
62
-        },
63
-        set(value) {
64
-            this._onaddstream = value;
65
-        }
66
-    });
67
-
68
-    /* eslint-enable indent, no-invalid-this */
69
-}
70
-
71
-_RTCPeerConnection.prototype = Object.create(RTCPeerConnection.prototype);
72
-_RTCPeerConnection.prototype.constructor = _RTCPeerConnection;
73
-
74
-_RTCPeerConnection.prototype._invokeOnaddstream = function(...args) {
75
-    const onaddstream = this._onaddstream;
76
-
77
-    return onaddstream && onaddstream.apply(this, args);
78
-};
79
-
80
-_RTCPeerConnection.prototype._invokeQueuedOnaddstream = function(q) {
81
-    q && q.forEach(args => {
82
-        try {
83
-            this._invokeOnaddstream(...args);
84
-        } catch (e) {
85
-            // TODO Determine whether the combination of the standard
86
-            // setRemoteDescription and onaddstream results in a similar
87
-            // swallowing of errors.
88
-            console.error(e);
89
-        }
90
-    });
91
-};
92
-
93
-_RTCPeerConnection.prototype._queueOnaddstream = function(...args) {
94
-    this._onaddstreamQueue.push(Array.from(args));
95
-};
96
-
97
-_RTCPeerConnection.prototype.setRemoteDescription = function(description) {
98
-
99
-    return (
100
-        _synthesizeIPv6Addresses(description)
101
-            .catch(reason => {
102
-                reason && console.error(reason);
103
-
104
-                return description;
105
-            })
106
-            .then(value => _setRemoteDescription.bind(this)(value)));
107
-
108
-};
109
-
110
-/**
111
- * Adapts react-native-webrtc's {@link RTCPeerConnection#setRemoteDescription}
112
- * implementation which uses the deprecated, callback-based version to the
113
- * {@code Promise}-based version.
114
- *
115
- * @param {RTCSessionDescription} description - The RTCSessionDescription
116
- * which specifies the configuration of the remote end of the connection.
117
- * @private
118
- * @private
119
- * @returns {Promise}
120
- */
121
-function _setRemoteDescription(description) {
122
-    return new Promise((resolve, reject) => {
123
-
124
-        /* eslint-disable no-invalid-this */
125
-
126
-        // Ensure I'm not remembering onaddstream invocations from previous
127
-        // setRemoteDescription calls. I shouldn't be but... anyway.
128
-        this._onaddstreamQueue = [];
129
-
130
-        RTCPeerConnection.prototype.setRemoteDescription.call(this, description)
131
-            .then((...args) => {
132
-                let q;
133
-
134
-                try {
135
-                    resolve(...args);
136
-                } finally {
137
-                    q = this._onaddstreamQueue;
138
-                    this._onaddstreamQueue = undefined;
139
-                }
140
-
141
-                this._invokeQueuedOnaddstream(q);
142
-            }, (...args) => {
143
-                this._onaddstreamQueue = undefined;
144
-
145
-                reject(...args);
146
-            });
147
-
148
-        /* eslint-enable no-invalid-this */
149
-    });
150
-}
151
-
152
-// XXX The function _synthesizeIPv6FromIPv4Address is not placed relative to the
153
-// other functions in the file according to alphabetical sorting rule of the
154
-// coding style. But eslint wants constants to be defined before they are used.
155
-
156
-/**
157
- * Synthesizes an IPv6 address from a specific IPv4 address.
158
- *
159
- * @param {string} ipv4 - The IPv4 address from which an IPv6 address is to be
160
- * synthesized.
161
- * @returns {Promise<?string>} A {@code Promise} which gets resolved with the
162
- * IPv6 address synthesized from the specified {@code ipv4} or a falsy value to
163
- * be treated as inability to synthesize an IPv6 address from the specified
164
- * {@code ipv4}.
165
- */
166
-const _synthesizeIPv6FromIPv4Address: string => Promise<?string> = (function() {
167
-    // POSIX.getaddrinfo
168
-    const { POSIX } = NativeModules;
169
-
170
-    if (POSIX) {
171
-        const { getaddrinfo } = POSIX;
172
-
173
-        if (typeof getaddrinfo === 'function') {
174
-            return ipv4 =>
175
-                getaddrinfo(/* hostname */ ipv4, /* servname */ undefined)
176
-                    .then(([ { ai_addr: ipv6 } ]) => ipv6);
177
-        }
178
-    }
179
-
180
-    // NAT64AddrInfo.getIPv6Address
181
-    const { NAT64AddrInfo } = NativeModules;
182
-
183
-    if (NAT64AddrInfo) {
184
-        const { getIPv6Address } = NAT64AddrInfo;
185
-
186
-        if (typeof getIPv6Address === 'function') {
187
-            return getIPv6Address;
188
-        }
189
-    }
190
-
191
-    // There's no POSIX.getaddrinfo or NAT64AddrInfo.getIPv6Address.
192
-    return () =>
193
-        Promise.reject(
194
-            'The impossible just happened! No POSIX.getaddrinfo or'
195
-                + ' NAT64AddrInfo.getIPv6Address!');
196
-})();
197
-
198
-/**
199
- * Synthesizes IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
200
- *
201
- * @param {RTCSessionDescription} sdp - The RTCSessionDescription which
202
- * specifies the configuration of the remote end of the connection.
203
- * @private
204
- * @returns {Promise}
205
- */
206
-function _synthesizeIPv6Addresses(sdp) {
207
-    return (
208
-        new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
209
-            .then(({ ips, lines }) =>
210
-                Promise.all(Array.from(ips.values()))
211
-                    .then(() => _synthesizeIPv6Addresses1(sdp, ips, lines))
212
-            ));
213
-}
214
-
215
-/* eslint-disable max-depth */
216
-
217
-/**
218
- * Begins the asynchronous synthesis of IPv6 addresses.
219
- *
220
- * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
221
- * for which IPv6 addresses will be synthesized.
222
- * @private
223
- * @returns {{
224
- *     ips: Map,
225
- *     lines: Array
226
- * }}
227
- */
228
-function _synthesizeIPv6Addresses0(sessionDescription) {
229
-    const sdp = sessionDescription.sdp;
230
-    let start = 0;
231
-    const lines = [];
232
-    const ips = new Map();
233
-
234
-    do {
235
-        const end = sdp.indexOf('\r\n', start);
236
-        let line;
237
-
238
-        if (end === -1) {
239
-            line = sdp.substring(start);
240
-
241
-            // Break out of the loop at the end of the iteration.
242
-            start = undefined;
243
-        } else {
244
-            line = sdp.substring(start, end);
245
-            start = end + 2;
246
-        }
247
-
248
-        if (line.startsWith('a=candidate:')) {
249
-            const candidate = line.split(' ');
250
-
251
-            if (candidate.length >= 10 && candidate[6] === 'typ') {
252
-                const ip4s = [ candidate[4] ];
253
-                let abort = false;
254
-
255
-                for (let i = 8; i < candidate.length; ++i) {
256
-                    if (candidate[i] === 'raddr') {
257
-                        ip4s.push(candidate[++i]);
258
-                        break;
259
-                    }
260
-                }
261
-
262
-                for (const ip of ip4s) {
263
-                    if (ip.indexOf(':') === -1) {
264
-                        ips.has(ip)
265
-                            || ips.set(ip, new Promise((resolve, reject) => {
266
-                                const v = ips.get(ip);
267
-
268
-                                if (v && typeof v === 'string') {
269
-                                    resolve(v);
270
-                                } else {
271
-                                    _synthesizeIPv6FromIPv4Address(ip).then(
272
-                                        value => {
273
-                                            if (!value
274
-                                                    || value.indexOf(':') === -1
275
-                                                    || value === ips.get(ip)) {
276
-                                                ips.delete(ip);
277
-                                            } else {
278
-                                                ips.set(ip, value);
279
-                                            }
280
-                                            resolve(value);
281
-                                        },
282
-                                        reject);
283
-                                }
284
-                            }));
285
-                    } else {
286
-                        abort = true;
287
-                        break;
288
-                    }
289
-                }
290
-                if (abort) {
291
-                    ips.clear();
292
-                    break;
293
-                }
294
-
295
-                line = candidate;
296
-            }
297
-        }
298
-
299
-        lines.push(line);
300
-    } while (start);
301
-
302
-    return {
303
-        ips,
304
-        lines
305
-    };
306
-}
307
-
308
-/* eslint-enable max-depth */
5
+import { synthesizeIPv6Addresses } from './ipv6utils';
309 6
 
310 7
 /**
311
- * Completes the asynchronous synthesis of IPv6 addresses.
312
- *
313
- * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
314
- * for which IPv6 addresses are being synthesized.
315
- * @param {Map} ips - A Map of IPv4 addresses found in the specified
316
- * sessionDescription to synthesized IPv6 addresses.
317
- * @param {Array} lines - The lines of the specified sessionDescription.
318
- * @private
319
- * @returns {RTCSessionDescription} A RTCSessionDescription that represents the
320
- * result of the synthesis of IPv6 addresses.
8
+ * Override PeerConnection to synthesize IPv6 addresses.
321 9
  */
322
-function _synthesizeIPv6Addresses1(sessionDescription, ips, lines) {
323
-    if (ips.size === 0) {
324
-        return sessionDescription;
10
+export default class RTCPeerConnection extends PC {
11
+
12
+    /**
13
+     * Synthesize IPv6 addresses before calling the underlying setRemoteDescription.
14
+     *
15
+     * @param {Object} description - SDP.
16
+     * @returns {Promise<undefined>} A promise which is resolved once the operation is complete.
17
+     */
18
+    async setRemoteDescription(description: Object) {
19
+        return super.setRemoteDescription(await synthesizeIPv6Addresses(description));
325 20
     }
326
-
327
-    for (let l = 0; l < lines.length; ++l) {
328
-        const candidate = lines[l];
329
-
330
-        if (typeof candidate !== 'string') {
331
-            let ip4 = candidate[4];
332
-            let ip6 = ips.get(ip4);
333
-
334
-            ip6 && (candidate[4] = ip6);
335
-
336
-            for (let i = 8; i < candidate.length; ++i) {
337
-                if (candidate[i] === 'raddr') {
338
-                    ip4 = candidate[++i];
339
-                    (ip6 = ips.get(ip4)) && (candidate[i] = ip6);
340
-                    break;
341
-                }
342
-            }
343
-
344
-            lines[l] = candidate.join(' ');
345
-        }
346
-    }
347
-
348
-    return new RTCSessionDescription({
349
-        sdp: lines.join('\r\n'),
350
-        type: sessionDescription.type
351
-    });
352 21
 }

+ 199
- 0
react/features/mobile/polyfills/ipv6utils.js View File

@@ -0,0 +1,199 @@
1
+// @flow
2
+
3
+import { NativeModules } from 'react-native';
4
+import { RTCSessionDescription } from 'react-native-webrtc';
5
+
6
+/**
7
+ * Synthesizes IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
8
+ *
9
+ * @param {RTCSessionDescription} sdp - The RTCSessionDescription which
10
+ * specifies the configuration of the remote end of the connection.
11
+ * @private
12
+ * @returns {Promise}
13
+ */
14
+export function synthesizeIPv6Addresses(sdp: RTCSessionDescription) {
15
+    return (
16
+        new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
17
+            .then(({ ips, lines }) =>
18
+                Promise.all(Array.from(ips.values()))
19
+                    .then(() => _synthesizeIPv6Addresses1(sdp, ips, lines))
20
+            ));
21
+}
22
+
23
+/* eslint-disable max-depth */
24
+
25
+/**
26
+ * Synthesizes an IPv6 address from a specific IPv4 address.
27
+ *
28
+ * @param {string} ipv4 - The IPv4 address from which an IPv6 address is to be
29
+ * synthesized.
30
+ * @returns {Promise<?string>} A {@code Promise} which gets resolved with the
31
+ * IPv6 address synthesized from the specified {@code ipv4} or a falsy value to
32
+ * be treated as inability to synthesize an IPv6 address from the specified
33
+ * {@code ipv4}.
34
+ */
35
+const _synthesizeIPv6FromIPv4Address: string => Promise<?string> = (function() {
36
+    // POSIX.getaddrinfo
37
+    const { POSIX } = NativeModules;
38
+
39
+    if (POSIX) {
40
+        const { getaddrinfo } = POSIX;
41
+
42
+        if (typeof getaddrinfo === 'function') {
43
+            return ipv4 =>
44
+                getaddrinfo(/* hostname */ ipv4, /* servname */ undefined)
45
+                    .then(([ { ai_addr: ipv6 } ]) => ipv6);
46
+        }
47
+    }
48
+
49
+    // NAT64AddrInfo.getIPv6Address
50
+    const { NAT64AddrInfo } = NativeModules;
51
+
52
+    if (NAT64AddrInfo) {
53
+        const { getIPv6Address } = NAT64AddrInfo;
54
+
55
+        if (typeof getIPv6Address === 'function') {
56
+            return getIPv6Address;
57
+        }
58
+    }
59
+
60
+    // There's no POSIX.getaddrinfo or NAT64AddrInfo.getIPv6Address.
61
+    return ip => Promise.resolve(ip);
62
+})();
63
+
64
+/**
65
+ * Begins the asynchronous synthesis of IPv6 addresses.
66
+ *
67
+ * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
68
+ * for which IPv6 addresses will be synthesized.
69
+ * @private
70
+ * @returns {{
71
+ *     ips: Map,
72
+ *     lines: Array
73
+ * }}
74
+ */
75
+function _synthesizeIPv6Addresses0(sessionDescription) {
76
+    const sdp = sessionDescription.sdp;
77
+    let start = 0;
78
+    const lines = [];
79
+    const ips = new Map();
80
+
81
+    do {
82
+        const end = sdp.indexOf('\r\n', start);
83
+        let line;
84
+
85
+        if (end === -1) {
86
+            line = sdp.substring(start);
87
+
88
+            // Break out of the loop at the end of the iteration.
89
+            start = undefined;
90
+        } else {
91
+            line = sdp.substring(start, end);
92
+            start = end + 2;
93
+        }
94
+
95
+        if (line.startsWith('a=candidate:')) {
96
+            const candidate = line.split(' ');
97
+
98
+            if (candidate.length >= 10 && candidate[6] === 'typ') {
99
+                const ip4s = [ candidate[4] ];
100
+                let abort = false;
101
+
102
+                for (let i = 8; i < candidate.length; ++i) {
103
+                    if (candidate[i] === 'raddr') {
104
+                        ip4s.push(candidate[++i]);
105
+                        break;
106
+                    }
107
+                }
108
+
109
+                for (const ip of ip4s) {
110
+                    if (ip.indexOf(':') === -1) {
111
+                        ips.has(ip)
112
+                            || ips.set(ip, new Promise((resolve, reject) => {
113
+                                const v = ips.get(ip);
114
+
115
+                                if (v && typeof v === 'string') {
116
+                                    resolve(v);
117
+                                } else {
118
+                                    _synthesizeIPv6FromIPv4Address(ip).then(
119
+                                        value => {
120
+                                            if (!value
121
+                                                    || value.indexOf(':') === -1
122
+                                                    || value === ips.get(ip)) {
123
+                                                ips.delete(ip);
124
+                                            } else {
125
+                                                ips.set(ip, value);
126
+                                            }
127
+                                            resolve(value);
128
+                                        },
129
+                                        reject);
130
+                                }
131
+                            }));
132
+                    } else {
133
+                        abort = true;
134
+                        break;
135
+                    }
136
+                }
137
+                if (abort) {
138
+                    ips.clear();
139
+                    break;
140
+                }
141
+
142
+                line = candidate;
143
+            }
144
+        }
145
+
146
+        lines.push(line);
147
+    } while (start);
148
+
149
+    return {
150
+        ips,
151
+        lines
152
+    };
153
+}
154
+
155
+/* eslint-enable max-depth */
156
+
157
+/**
158
+ * Completes the asynchronous synthesis of IPv6 addresses.
159
+ *
160
+ * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
161
+ * for which IPv6 addresses are being synthesized.
162
+ * @param {Map} ips - A Map of IPv4 addresses found in the specified
163
+ * sessionDescription to synthesized IPv6 addresses.
164
+ * @param {Array} lines - The lines of the specified sessionDescription.
165
+ * @private
166
+ * @returns {RTCSessionDescription} A RTCSessionDescription that represents the
167
+ * result of the synthesis of IPv6 addresses.
168
+ */
169
+function _synthesizeIPv6Addresses1(sessionDescription, ips, lines) {
170
+    if (ips.size === 0) {
171
+        return sessionDescription;
172
+    }
173
+
174
+    for (let l = 0; l < lines.length; ++l) {
175
+        const candidate = lines[l];
176
+
177
+        if (typeof candidate !== 'string') {
178
+            let ip4 = candidate[4];
179
+            let ip6 = ips.get(ip4);
180
+
181
+            ip6 && (candidate[4] = ip6);
182
+
183
+            for (let i = 8; i < candidate.length; ++i) {
184
+                if (candidate[i] === 'raddr') {
185
+                    ip4 = candidate[++i];
186
+                    (ip6 = ips.get(ip4)) && (candidate[i] = ip6);
187
+                    break;
188
+                }
189
+            }
190
+
191
+            lines[l] = candidate.join(' ');
192
+        }
193
+    }
194
+
195
+    return new RTCSessionDescription({
196
+        sdp: lines.join('\r\n'),
197
+        type: sessionDescription.type
198
+    });
199
+}

Loading…
Cancel
Save