@@ -33,6 +33,10 @@ const errorCantConnectTo = vAPI.i18n('errorCantConnectTo');
3333
3434const api = { } ;
3535
36+ // A hint for various pieces of code to take measures if possible to save
37+ // bandwidth of remote servers.
38+ let remoteServerFriendly = false ;
39+
3640/******************************************************************************/
3741
3842const observers = [ ] ;
@@ -157,14 +161,15 @@ api.fetchText = async function(url) {
157161 // https://github.com/gorhill/uBlock/issues/2592
158162 // Force browser cache to be bypassed, but only for resources which have
159163 // been fetched more than one hour ago.
160- //
161164 // https://github.com/uBlockOrigin/uBlock-issues/issues/682#issuecomment-515197130
162165 // Provide filter list authors a way to completely bypass
163166 // the browser cache.
164167 // https://github.com/gorhill/uBlock/commit/048bfd251c9b#r37972005
165168 // Use modulo prime numbers to avoid generating the same token at the
166169 // same time across different days.
167- if ( isExternal ) {
170+ // Do not bypass browser cache if we are asked to be gentle on remote
171+ // servers.
172+ if ( isExternal && remoteServerFriendly !== true ) {
168173 const cacheBypassToken =
169174 µBlock . hiddenSettings . updateAssetBypassBrowserCache
170175 ? Math . floor ( Date . now ( ) / 1000 ) % 86413
@@ -743,6 +748,19 @@ const getRemote = async function(assetKey) {
743748 contentURLs = assetDetails . contentURL . slice ( 0 ) ;
744749 }
745750
751+ // If asked to be gentle on remote servers, favour using dedicated CDN
752+ // servers. If more than one CDN server is present, randomly shuffle the
753+ // set of servers so as to spread the bandwidth burden.
754+ if ( remoteServerFriendly && Array . isArray ( assetDetails . cdnURLs ) ) {
755+ const cdnURLs = assetDetails . cdnURLs . slice ( ) ;
756+ for ( let i = 0 , n = cdnURLs . length ; i < n ; i ++ ) {
757+ const j = Math . floor ( Math . random ( ) * n ) ;
758+ if ( j === i ) { continue ; }
759+ [ cdnURLs [ j ] , cdnURLs [ i ] ] = [ cdnURLs [ i ] , cdnURLs [ j ] ] ;
760+ }
761+ contentURLs . unshift ( ...cdnURLs ) ;
762+ }
763+
746764 for ( const contentURL of contentURLs ) {
747765 if ( reIsExternalPath . test ( contentURL ) === false ) { continue ; }
748766
@@ -756,18 +774,17 @@ const getRemote = async function(assetKey) {
756774 if ( result . statusCode === 0 ) {
757775 error = 'network error' ;
758776 }
759- registerAssetSource (
760- assetKey ,
761- { error : { time : Date . now ( ) , error } }
762- ) ;
777+ registerAssetSource ( assetKey , {
778+ error : { time : Date . now ( ) , error }
779+ } ) ;
763780 continue ;
764781 }
765782
766783 // Success
767- assetCacheWrite (
768- assetKey ,
769- { content : result . content , url : contentURL }
770- ) ;
784+ assetCacheWrite ( assetKey , {
785+ content : result . content ,
786+ url : contentURL
787+ } ) ;
771788 registerAssetSource ( assetKey , { error : undefined } ) ;
772789 return reportBack ( result . content ) ;
773790 }
@@ -835,9 +852,10 @@ const updaterAssetDelayDefault = 120000;
835852const updaterUpdated = [ ] ;
836853const updaterFetched = new Set ( ) ;
837854
838- let updaterStatus ,
839- updaterTimer ,
840- updaterAssetDelay = updaterAssetDelayDefault ;
855+ let updaterStatus ;
856+ let updaterTimer ;
857+ let updaterAssetDelay = updaterAssetDelayDefault ;
858+ let updaterAuto = false ;
841859
842860const updateFirst = function ( ) {
843861 updaterStatus = 'updating' ;
@@ -861,25 +879,22 @@ const updateNext = async function() {
861879 if ( updaterFetched . has ( assetKey ) ) { continue ; }
862880 const cacheEntry = cacheDict [ assetKey ] ;
863881 if (
864- cacheEntry &&
882+ ( cacheEntry instanceof Object ) &&
865883 ( cacheEntry . writeTime + assetEntry . updateAfter * 86400000 ) > now
866884 ) {
867885 continue ;
868886 }
869887 if (
870- fireNotification (
871- 'before-asset-updated' ,
872- { assetKey : assetKey , type : assetEntry . content }
873- ) === true
888+ fireNotification ( 'before-asset-updated' , {
889+ assetKey ,
890+ type : assetEntry . content
891+ } ) === true
874892 ) {
875893 assetKeyToUpdate = assetKey ;
876894 break ;
877895 }
878896 // This will remove a cached asset when it's no longer in use.
879- if (
880- cacheEntry &&
881- cacheEntry . readTime < assetCacheRegistryStartTime
882- ) {
897+ if ( cacheEntry && cacheEntry . readTime < assetCacheRegistryStartTime ) {
883898 assetCacheRemove ( assetKey ) ;
884899 }
885900 }
@@ -888,7 +903,13 @@ const updateNext = async function() {
888903 }
889904 updaterFetched . add ( assetKeyToUpdate ) ;
890905
906+ // In auto-update context, be gentle on remote servers.
907+ remoteServerFriendly = updaterAuto ;
908+
891909 const result = await getRemote ( assetKeyToUpdate ) ;
910+
911+ remoteServerFriendly = false ;
912+
892913 if ( result . content !== '' ) {
893914 updaterUpdated . push ( result . assetKey ) ;
894915 if ( result . assetKey === 'assets.json' ) {
@@ -912,10 +933,11 @@ const updateDone = function() {
912933
913934api . updateStart = function ( details ) {
914935 const oldUpdateDelay = updaterAssetDelay ;
915- const newUpdateDelay = typeof details . delay === 'number' ?
916- details . delay :
917- updaterAssetDelayDefault ;
936+ const newUpdateDelay = typeof details . delay === 'number'
937+ ? details . delay
938+ : updaterAssetDelayDefault ;
918939 updaterAssetDelay = Math . min ( oldUpdateDelay , newUpdateDelay ) ;
940+ updaterAuto = details . auto === true ;
919941 if ( updaterStatus !== undefined ) {
920942 if ( newUpdateDelay < oldUpdateDelay ) {
921943 clearTimeout ( updaterTimer ) ;
0 commit comments