webpeerjs
Version:
WebPEER is a Decentralized P2P Network in the Browser
269 lines (223 loc) • 7.75 kB
JavaScript
//! WebPEER.js -- https://github.com/nuzulul/webpeerjs
import * as config from './config.js'
import { Peer as PBPeer } from './peer.js'
import { Key } from 'interface-datastore'
import { sha256 } from 'multiformats/hashes/sha2'
import { multiaddr } from '@multiformats/multiaddr'
import { pipe } from 'it-pipe'
import { lpStream } from 'it-length-prefixed-stream'
import * as lp from 'it-length-prefixed'
import map from 'it-map'
export { Key }
export { multiaddr }
export { pipe }
export { lpStream }
export { lp }
export { map }
const prefix = config.CONFIG_PREFIX
export const mkErr = msg => new Error(`${prefix}: ${msg}`)
export function mkDebug(msg){
if(config.CONFIG_DEBUG_ENABLED)console.debug(msg)
return
}
export {PBPeer}
export function uint8ArrayToString(uint8Array){
const string = new TextDecoder().decode(uint8Array)
return string
}
export function uint8ArrayFromString(string){
const uint8Array = new TextEncoder().encode(string)
return uint8Array
}
export async function first(farr){
for await(const data of farr){
return data
//break
}
}
//browser detector
//this code comes from https://stackoverflow.com/a/9851769
// Opera 8.0+
const isOpera = (!!window.opr && !!window.opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
// Firefox 1.0+
const isFirefox = typeof InstallTrigger !== 'undefined';
// Safari 3.0+ "[object HTMLElementConstructor]"
const isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && window['safari'].pushNotification));
// Internet Explorer 6-11
//const isIE = /*@cc_on!@*/false || !!document.documentMode;
// Edge 20+
//const isEdge = !isIE && !!window.StyleMedia;
// Chrome 1 - 79
const isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
// Edge (based on chromium) detection
const isEdgeChromium = isChrome && (navigator.userAgent.indexOf("Edg") != -1);
// Blink engine detection
const isBlink = (isChrome || isOpera) && !!window.CSS;
export const browser = {isOpera,isFirefox,isSafari,isChrome,isEdgeChromium,isBlink}
//Add id to pupsub message
export async function msgIdFnStrictNoSign(msg){
var enc = new TextEncoder()
const signedMessage = msg
const encodedSeqNum = enc.encode(signedMessage.sequenceNumber.toString())
return await sha256.encode(encodedSeqNum)
}
let totals = {
readyErrored: 0,
noiseErrored: 0,
upgradeErrored: 0,
readyTimedout: 0,
noiseTimedout: 0,
success: 0
}
let stats = {
pending: 0,
open: 0,
ready_error: 0,
noise_error: 0,
upgrade_error: 0,
ready_timeout: 0,
noise_timeout: 0,
close: 0,
abort: 0,
remote_close: 0,
}
let lastStats = {
pending: 0,
ready_error: 0,
noise_error: 0,
upgrade_error: 0,
close: 0,
remote_close: 0,
ready: 0,
abort: 0,
ready_timeout: 0,
noise_timeout: 0,
open: 0
}
let fail = 0
let lastfail = 0
let isDialEnabled = true
let lastfailtreshold = 0
let isAutoDialEnabled = true
let lastfailtresholdauto = 0
export function metrics(data){
if(isFirefox){
return {isDialEnabled,isAutoDialEnabled}
}
try{
const webTransportEvents = data.libp2p_webtransport_dialer_events_total
const newPending = (webTransportEvents.pending ?? 0) - (lastStats.pending ?? 0)
const newReadyError = (webTransportEvents.ready_error ?? 0) - (lastStats.ready_error ?? 0)
const newNoiseError = (webTransportEvents.noise_error ?? 0) - (lastStats.noise_error ?? 0)
const newUpgradeError = (webTransportEvents.upgrade_error ?? 0) - (lastStats.upgrade_error ?? 0)
const newClose = (webTransportEvents.close ?? 0) - (lastStats.close ?? 0)
const newReady = (webTransportEvents.ready ?? 0) - (lastStats.ready ?? 0)
const newAbort = (webTransportEvents.abort ?? 0) - (lastStats.abort ?? 0)
const newReadyTimeout = (webTransportEvents.ready_timeout ?? 0) - (lastStats.ready_timeout ?? 0)
const newNoiseTimeout = (webTransportEvents.noise_timeout ?? 0) - (lastStats.noise_timeout ?? 0)
const newOpen = (webTransportEvents.open ?? 0) - (lastStats.open ?? 0)
const newRemoteClose = (webTransportEvents.remote_close ?? 0) - (lastStats.remote_close ?? 0)
stats.pending += newPending
stats.pending -= newReadyTimeout
stats.pending -= newNoiseTimeout
stats.pending -= newReadyError
stats.pending -= newNoiseError
stats.pending -= newUpgradeError
stats.pending -= newOpen
stats.open += newOpen
stats.open -= newClose
stats.open -= newRemoteClose
stats.open -= newAbort
stats.ready_error = newReadyError
stats.noise_error = newNoiseError
stats.upgrade_error = newUpgradeError
stats.ready_timeout = newReadyTimeout
stats.noise_timeout = newNoiseTimeout
stats.close = newClose
stats.abort = newAbort
stats.remote_close = newRemoteClose
totals.success += newReady
totals.readyErrored += newReadyError
totals.noiseErrored += newNoiseError
totals.upgradeErrored += newUpgradeError
totals.readyTimedout += newReadyTimeout
totals.noiseTimedout += newNoiseTimeout
const errors = totals.readyErrored + totals.noiseErrored + totals.upgradeErrored
const timeouts = totals.readyTimedout + totals.noiseTimedout
//const failureRate = ((errors + timeouts) / (errors + timeouts + totals.success) * 100).toFixed(2)
lastStats = webTransportEvents
fail = errors+timeouts
//const treshold = errors+timeouts+stats.open+stats.pending
//console.log('fail',fail)
//detect fail timeout, in chrome fail webtransport get reset after 5 minutes
if(lastfail>fail){
setTimeout(()=>{
if(isAutoDialEnabled){
lastfailtresholdauto = lastfailtresholdauto+(lastfail-fail)
}
if(isDialEnabled){
lastfailtreshold = lastfailtreshold+(lastfail-fail)
}
},5*60*1000)
}
lastfail = fail
if ((fail-lastfailtreshold)>config.CONFIG_DIAL_MAX_ERROR_LIMIT){
if(isDialEnabled){
isDialEnabled = false
console.warn('Peer dial is temporary disabled')
if(isAutoDialEnabled){
isAutoDialEnabled = false
console.warn('Peer autodial is temporary disabled')
}
setTimeout(()=>{
if(!isDialEnabled){
isDialEnabled = true
lastfailtreshold = fail
console.warn('Peer dial is resumed')
if(!isAutoDialEnabled){
isAutoDialEnabled = true
lastfailtresholdauto = fail
console.warn('Peer autodial is resumed')
}
}
},6*60*1000)
}
}
if ((fail-lastfailtresholdauto)>config.CONFIG_AUTODIAL_MAX_ERROR_LIMIT){
if(isAutoDialEnabled){
isAutoDialEnabled = false
console.warn('Peer autodial is temporary disabled')
const last = fail
setTimeout(()=>{
if(!isAutoDialEnabled && isDialEnabled){
isAutoDialEnabled = true
lastfailtresholdauto = fail
console.warn('Peer autodial is resumed')
if(isDialEnabled){
lastfailtreshold = last
}
}
},6*60*1000)
}
}
return {isDialEnabled,isAutoDialEnabled}
}
catch{
console.debug('Metrics error')
}
}
/*
onunhandledrejection = function(evt) {
console.warn(evt.reason);
return
}
*/
export async function getDigest(){
const buf = new TextEncoder().encode(Math.random().toString())
const digest = await sha256.encode(buf)
return digest
}
//detect webtransport
if(!WebTransport){
throw mkErr('It seems that your browser does not support the required WebTransport feature')
}