@@ -84,6 +84,7 @@ import {
8484 sleep ,
8585 supportsAddTrack ,
8686 supportsTransceiver ,
87+ toHttpUrl ,
8788} from './utils' ;
8889
8990const lossyDataChannel = '_lossy' ;
@@ -204,6 +205,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
204205
205206 private midToTrackId : { [ key : string ] : string } = { } ;
206207
208+ /** used to indicate whether the browser is currently waiting to reconnect */
209+ private isWaitingForNetworkReconnect : boolean = false ;
210+
207211 constructor ( private options : InternalRoomOptions ) {
208212 super ( ) ;
209213 this . log = getLogger ( options . loggerName ?? LoggerNames . Engine ) ;
@@ -1619,23 +1623,62 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
16191623 this . reconnectAttempts = 0 ;
16201624 }
16211625
1622- private handleBrowserOnLine = ( ) => {
1623- // in case the engine is currently reconnecting, attempt a reconnect immediately after the browser state has changed to 'onLine'
1624- if ( this . client . currentState === SignalConnectionState . RECONNECTING ) {
1626+ private handleBrowserOnLine = async ( ) => {
1627+ if ( ! this . url ) {
1628+ return ;
1629+ }
1630+ const hasNetworkConnection = await fetch ( toHttpUrl ( this . url ! ) , { method : 'HEAD' } )
1631+ . then ( ( resp ) => resp . ok )
1632+ . catch ( ( ) => false ) ;
1633+
1634+ if ( ! hasNetworkConnection ) {
1635+ return ;
1636+ }
1637+ this . log . info ( 'detected network reconnected' ) ;
1638+
1639+ if (
1640+ // in case the engine is currently reconnecting, attempt a reconnect immediately after the browser state has changed to 'onLine'
1641+ this . client . currentState === SignalConnectionState . RECONNECTING ||
1642+ // also if the browser went offline before and the engine still thinks it's in a connected state, treat it as a network interruption that we haven't noticed yet
1643+ ( this . isWaitingForNetworkReconnect &&
1644+ this . client . currentState === SignalConnectionState . CONNECTED )
1645+ ) {
16251646 this . clearReconnectTimeout ( ) ;
16261647 this . attemptReconnect ( ReconnectReason . RR_SIGNAL_DISCONNECTED ) ;
1648+ this . isWaitingForNetworkReconnect = false ;
1649+ }
1650+ } ;
1651+
1652+ private handleBrowserOffline = async ( ) => {
1653+ if ( ! this . url ) {
1654+ return ;
1655+ }
1656+ try {
1657+ await Promise . race ( [
1658+ fetch ( toHttpUrl ( this . url ) , { method : 'HEAD' } ) ,
1659+ // if there's no internet connection the fetch rejects immediately, so we only use a short timeout here
1660+ sleep ( 4_000 ) . then ( ( ) => Promise . reject ( ) ) ,
1661+ ] ) ;
1662+ } catch ( e ) {
1663+ // only set if the browser still thinks it's offline after the request failed
1664+ if ( window . navigator . onLine === false ) {
1665+ this . log . info ( 'detected network interruption' ) ;
1666+ this . isWaitingForNetworkReconnect = true ;
1667+ }
16271668 }
16281669 } ;
16291670
16301671 private registerOnLineListener ( ) {
16311672 if ( isWeb ( ) ) {
16321673 window . addEventListener ( 'online' , this . handleBrowserOnLine ) ;
1674+ window . addEventListener ( 'offline' , this . handleBrowserOffline ) ;
16331675 }
16341676 }
16351677
16361678 private deregisterOnLineListener ( ) {
16371679 if ( isWeb ( ) ) {
16381680 window . removeEventListener ( 'online' , this . handleBrowserOnLine ) ;
1681+ window . removeEventListener ( 'offline' , this . handleBrowserOffline ) ;
16391682 }
16401683 }
16411684
0 commit comments