@@ -52,8 +52,8 @@ class WebSocketChannel {
5252 return ;
5353 }
5454 }
55- this . _url = scheme + '://' + config . host + ':' + config . port ;
56- this . _ws = new WebSocket ( this . _url ) ;
55+
56+ this . _ws = createWebSocket ( scheme , config . url ) ;
5757 this . _ws . binaryType = "arraybuffer" ;
5858
5959 let self = this ;
@@ -65,8 +65,8 @@ class WebSocketChannel {
6565 }
6666 } ;
6767 this . _ws . onopen = function ( ) {
68- // Connected! Cancel connection timeout
69- clearTimeout ( self . _connectionTimeoutId ) ;
68+ // Connected! Cancel the connection timeout
69+ self . _clearConnectionTimeout ( ) ;
7070
7171 // Drain all pending messages
7272 let pending = self . _pending ;
@@ -85,7 +85,7 @@ class WebSocketChannel {
8585 this . _ws . onerror = this . _handleConnectionError ;
8686
8787 this . _connectionTimeoutFired = false ;
88- this . _connectionTimeoutId = this . _setupConnectionTimeout ( config ) ;
88+ this . _connectionTimeoutId = this . _setupConnectionTimeout ( ) ;
8989 }
9090
9191 _handleConnectionError ( ) {
@@ -141,6 +141,7 @@ class WebSocketChannel {
141141 */
142142 close ( cb = ( ( ) => null ) ) {
143143 this . _open = false ;
144+ this . _clearConnectionTimeout ( ) ;
144145 this . _ws . close ( ) ;
145146 this . _ws . onclose = cb ;
146147 }
@@ -164,9 +165,73 @@ class WebSocketChannel {
164165 }
165166 return null ;
166167 }
168+
169+ /**
170+ * Remove active connection timeout, if any.
171+ * @private
172+ */
173+ _clearConnectionTimeout ( ) {
174+ const timeoutId = this . _connectionTimeoutId ;
175+ if ( timeoutId || timeoutId === 0 ) {
176+ this . _connectionTimeoutFired = false ;
177+ this . _connectionTimeoutId = null ;
178+ clearTimeout ( timeoutId ) ;
179+ }
180+ }
167181}
168182
169183let available = typeof WebSocket !== 'undefined' ;
170184let _websocketChannelModule = { channel : WebSocketChannel , available : available } ;
171185
186+ function createWebSocket ( scheme , parsedUrl ) {
187+ const url = scheme + '://' + parsedUrl . hostAndPort ;
188+
189+ try {
190+ return new WebSocket ( url ) ;
191+ } catch ( error ) {
192+ if ( isIPv6AddressIssueOnWindows ( error , parsedUrl ) ) {
193+
194+ // WebSocket in IE and Edge browsers on Windows do not support regular IPv6 address syntax because they contain ':'.
195+ // It's an invalid character for UNC (https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_UNC_path_names)
196+ // and Windows requires IPv6 to be changes in the following way:
197+ // 1) replace all ':' with '-'
198+ // 2) replace '%' with 's' for link-local address
199+ // 3) append '.ipv6-literal.net' suffix
200+ // only then resulting string can be considered a valid IPv6 address. Yes, this is extremely weird!
201+ // For more details see:
202+ // https://social.msdn.microsoft.com/Forums/ie/en-US/06cca73b-63c2-4bf9-899b-b229c50449ff/whether-ie10-websocket-support-ipv6?forum=iewebdevelopment
203+ // https://www.itdojo.com/ipv6-addresses-and-unc-path-names-overcoming-illegal/
204+ // Creation of WebSocket with unconverted address results in SyntaxError without message or stacktrace.
205+ // That is why here we "catch" SyntaxError and rewrite IPv6 address if needed.
206+
207+ const windowsFriendlyUrl = asWindowsFriendlyIPv6Address ( scheme , parsedUrl ) ;
208+ return new WebSocket ( windowsFriendlyUrl ) ;
209+ } else {
210+ throw error ;
211+ }
212+ }
213+ }
214+
215+ function isIPv6AddressIssueOnWindows ( error , parsedUrl ) {
216+ return error . name === 'SyntaxError' && isIPv6Address ( parsedUrl ) ;
217+ }
218+
219+ function isIPv6Address ( parsedUrl ) {
220+ const hostAndPort = parsedUrl . hostAndPort ;
221+ return hostAndPort . charAt ( 0 ) === '[' && hostAndPort . indexOf ( ']' ) !== - 1 ;
222+ }
223+
224+ function asWindowsFriendlyIPv6Address ( scheme , parsedUrl ) {
225+ // replace all ':' with '-'
226+ const hostWithoutColons = parsedUrl . host . replace ( new RegExp ( ':' , 'g' ) , '-' ) ;
227+
228+ // replace '%' with 's' for link-local IPv6 address like 'fe80::1%lo0'
229+ const hostWithoutPercent = hostWithoutColons . replace ( '%' , 's' ) ;
230+
231+ // append magic '.ipv6-literal.net' suffix
232+ const ipv6Host = hostWithoutPercent + '.ipv6-literal.net' ;
233+
234+ return `${ scheme } ://${ ipv6Host } :${ parsedUrl . port } ` ;
235+ }
236+
172237export default _websocketChannelModule
0 commit comments